mirror of https://github.com/bsnes-emu/bsnes.git
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:
parent
5d135b556d
commit
41e127a07c
|
@ -13,7 +13,7 @@ using namespace nall;
|
|||
|
||||
namespace Emulator {
|
||||
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 License = "GPLv3";
|
||||
static const string Website = "https://byuu.org/";
|
||||
|
|
|
@ -11,6 +11,9 @@ extern Input input;
|
|||
#include <emulator/emulator.hpp>
|
||||
extern unique_pointer<Emulator::Interface> emulator;
|
||||
|
||||
#include <nall/encode/rle.hpp>
|
||||
#include <nall/decode/rle.hpp>
|
||||
|
||||
#include "program/program.hpp"
|
||||
#include "input/input.hpp"
|
||||
#include "presentation/presentation.hpp"
|
||||
|
|
|
@ -448,9 +448,8 @@ auto Presentation::updateStateMenus() -> void {
|
|||
for(auto& action : saveState.actions()) {
|
||||
if(auto item = action.cast<MenuItem>()) {
|
||||
if(auto name = item.property("name")) {
|
||||
if(states.find(name)) {
|
||||
auto timestamp = program.stateTimestamp(item.property("name"));
|
||||
item.setText({item.property("title"), " [", chrono::local::datetime(timestamp), "]"});
|
||||
if(auto offset = states.find([&](auto& state) { return state.name == name; })) {
|
||||
item.setText({item.property("title"), " [", chrono::local::datetime(states[*offset].date), "]"});
|
||||
} else {
|
||||
item.setText({item.property("title"), " [Empty]"});
|
||||
}
|
||||
|
@ -461,10 +460,9 @@ auto Presentation::updateStateMenus() -> void {
|
|||
for(auto& action : loadState.actions()) {
|
||||
if(auto item = action.cast<MenuItem>()) {
|
||||
if(auto name = item.property("name")) {
|
||||
if(states.find(name)) {
|
||||
auto timestamp = program.stateTimestamp(item.property("name"));
|
||||
if(auto offset = states.find([&](auto& state) { return state.name == name; })) {
|
||||
item.setEnabled(true);
|
||||
item.setText({item.property("title"), " [", chrono::local::datetime(timestamp), "]"});
|
||||
item.setText({item.property("title"), " [", chrono::local::datetime(states[*offset].date), "]"});
|
||||
} else {
|
||||
item.setEnabled(false);
|
||||
item.setText({item.property("title"), " [Empty]"});
|
||||
|
|
|
@ -73,14 +73,14 @@ auto Program::screenshotPath() -> string {
|
|||
if(!emulator->loaded()) return "";
|
||||
auto location = gamePath();
|
||||
if(location.endsWith("/")) {
|
||||
directory::create({location, "bsnes/screenshots/"});
|
||||
location = {location, "bsnes/screenshots/capture"};
|
||||
location = {location, "bsnes/screenshots/"};
|
||||
directory::create(location);
|
||||
} else {
|
||||
location = path("Screenshots", location);
|
||||
location = {path("Screenshots", location), "-"};
|
||||
}
|
||||
for(uint n : range(1, 999)) {
|
||||
string filename = {location, ".", pad(n, 3, '0'), ".bmp"};
|
||||
for(uint n : range(1, 1000)) {
|
||||
string filename = {location, pad(n, 3, '0'), ".bmp"};
|
||||
if(!file::exists(filename)) return filename;
|
||||
}
|
||||
return {location, ".000.bmp"};
|
||||
return {location, "000.bmp"};
|
||||
}
|
||||
|
|
|
@ -196,20 +196,20 @@ auto Program::videoRefresh(uint display, const uint32* data, uint pitch, uint wi
|
|||
uint32_t* output;
|
||||
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;
|
||||
if(presentation.overscanCropping.checked()) {
|
||||
if(height == 240) data += 8 * pitch, height -= 16;
|
||||
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)) {
|
||||
length >>= 2;
|
||||
|
||||
|
|
|
@ -49,8 +49,14 @@ struct Program : Emulator::Platform {
|
|||
auto screenshotPath() -> string;
|
||||
|
||||
//states.cpp
|
||||
auto availableStates(string type) -> vector<string>;
|
||||
auto stateTimestamp(string filename) -> uint64_t;
|
||||
struct State {
|
||||
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 saveState(string filename) -> bool;
|
||||
auto saveUndoState() -> bool;
|
||||
|
|
|
@ -1,53 +1,42 @@
|
|||
auto Program::availableStates(string type) -> vector<string> {
|
||||
vector<string> result;
|
||||
const uint Program::State::Signature = 0x5a22'0000;
|
||||
|
||||
auto Program::availableStates(string type) -> vector<State> {
|
||||
vector<State> result;
|
||||
if(!emulator->loaded()) return result;
|
||||
|
||||
if(gamePath().endsWith("/")) {
|
||||
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 {
|
||||
Decode::ZIP input;
|
||||
if(input.open(statePath())) {
|
||||
vector<string> filenames;
|
||||
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;
|
||||
}
|
||||
|
||||
auto Program::stateTimestamp(string filename) -> uint64_t {
|
||||
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 {
|
||||
auto Program::hasState(string filename) -> bool {
|
||||
if(!emulator->loaded()) return false;
|
||||
|
||||
string prefix = Location::file(filename);
|
||||
vector<uint8_t> memory;
|
||||
if(gamePath().endsWith("/")) {
|
||||
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("/")) {
|
||||
string location = {statePath(), filename, ".bst"};
|
||||
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") 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;
|
||||
return showMessage({"Loaded [", prefix, "]"}), true;
|
||||
} else {
|
||||
|
@ -76,15 +73,32 @@ auto Program::loadState(string filename) -> bool {
|
|||
|
||||
auto Program::saveState(string filename) -> bool {
|
||||
if(!emulator->loaded()) return false;
|
||||
|
||||
string prefix = Location::file(filename);
|
||||
|
||||
serializer s = emulator->serialize();
|
||||
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("/")) {
|
||||
string location = {statePath(), filename, ".bst"};
|
||||
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 {
|
||||
string location = {filename, ".bst"};
|
||||
|
||||
|
@ -105,10 +119,11 @@ auto Program::saveState(string filename) -> bool {
|
|||
for(auto& state : states) {
|
||||
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();
|
||||
stateManager.stateEvent(filename);
|
||||
return showMessage({"Saved [", prefix, "]"}), true;
|
||||
}
|
||||
|
||||
|
@ -132,10 +147,11 @@ auto Program::saveRedoState() -> bool {
|
|||
|
||||
auto Program::removeState(string filename) -> bool {
|
||||
if(!emulator->loaded()) return false;
|
||||
bool result = false;
|
||||
|
||||
if(gamePath().endsWith("/")) {
|
||||
string location = {statePath(), filename, ".bst"};
|
||||
return file::remove(location);
|
||||
result = file::remove(location);
|
||||
} else {
|
||||
bool found = false;
|
||||
string location = {filename, ".bst"};
|
||||
|
@ -162,21 +178,28 @@ auto Program::removeState(string filename) -> bool {
|
|||
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;
|
||||
bool result = false;
|
||||
|
||||
if(gamePath().endsWith("/")) {
|
||||
from = {statePath(), from, ".bst"};
|
||||
to = {statePath(), to, ".bst."};
|
||||
return file::rename(from, to);
|
||||
string from = {statePath(), from_, ".bst"};
|
||||
string to = {statePath(), to_, ".bst"};
|
||||
result = file::rename(from, to);
|
||||
} else {
|
||||
bool found = false;
|
||||
from = {from, ".bst"};
|
||||
to = {to, ".bst"};
|
||||
string from = {from_, ".bst"};
|
||||
string to = {to_, ".bst"};
|
||||
|
||||
struct State { string name; time_t timestamp; vector<uint8_t> memory; };
|
||||
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);
|
||||
}
|
||||
|
||||
return found;
|
||||
result = found;
|
||||
}
|
||||
|
||||
if(result) {
|
||||
stateManager.stateEvent(to_);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -34,9 +34,24 @@ auto Program::updateStatus() -> void {
|
|||
auto Program::captureScreenshot() -> bool {
|
||||
if(emulator->loaded() && screenshot.data) {
|
||||
if(auto filename = screenshotPath()) {
|
||||
if(Encode::BMP::create(filename,
|
||||
(const uint32_t*)screenshot.data, screenshot.pitch, screenshot.width, screenshot.height, false
|
||||
)) {
|
||||
image capture;
|
||||
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), "]"});
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ auto DriverSettings::create() -> void {
|
|||
settings["Video/Flush"].setValue(videoFlushToggle.checked());
|
||||
program.updateVideoFlush();
|
||||
});
|
||||
videoSpacer.setColor({192, 192, 192});
|
||||
|
||||
audioLabel.setText("Audio").setFont(Font().setBold());
|
||||
audioLayout.setSize({2, 2});
|
||||
|
@ -51,6 +52,7 @@ auto DriverSettings::create() -> void {
|
|||
settings["Audio/Dynamic"].setValue(audioDynamicToggle.checked());
|
||||
program.updateAudioDynamic();
|
||||
});
|
||||
audioSpacer.setColor({192, 192, 192});
|
||||
|
||||
inputLabel.setText("Input").setFont(Font().setBold());
|
||||
inputLayout.setSize({2, 1});
|
||||
|
|
|
@ -39,6 +39,7 @@ auto EmulatorSettings::create() -> void {
|
|||
settings["UserInterface/SuppressScreenSaver"].setValue(suppressScreenSaver.checked());
|
||||
Application::setScreenSaver(!suppressScreenSaver.checked());
|
||||
});
|
||||
optionsSpacer.setColor({192, 192, 192});
|
||||
|
||||
hacksLabel.setText("Hacks").setFont(Font().setBold());
|
||||
fastPPUOption.setText("Fast PPU").setChecked(settings["Emulator/Hack/FastPPU"].boolean()).onToggle([&] {
|
||||
|
|
|
@ -4,6 +4,7 @@ auto HotkeySettings::create() -> void {
|
|||
|
||||
layout.setPadding(5);
|
||||
mappingList.setBatchable();
|
||||
mappingList.setHeadered();
|
||||
mappingList.onActivate([&] {
|
||||
if(assignButton.enabled()) assignButton.doActivate();
|
||||
});
|
||||
|
@ -25,10 +26,8 @@ auto HotkeySettings::create() -> void {
|
|||
|
||||
auto HotkeySettings::reloadMappings() -> void {
|
||||
mappingList.reset();
|
||||
mappingList.append(TableViewHeader().setVisible()
|
||||
.append(TableViewColumn().setText("Name"))
|
||||
.append(TableViewColumn().setText("Mapping").setExpandable())
|
||||
);
|
||||
mappingList.append(TableViewColumn().setText("Name"));
|
||||
mappingList.append(TableViewColumn().setText("Mapping").setExpandable());
|
||||
for(auto& hotkey : inputManager.hotkeys) {
|
||||
mappingList.append(TableViewItem()
|
||||
.append(TableViewCell().setText(hotkey.name).setFont(Font().setBold()).setBackgroundColor({240, 240, 255}))
|
||||
|
|
|
@ -20,6 +20,7 @@ auto InputSettings::create() -> void {
|
|||
inputManager.turboFrequency = frequency;
|
||||
});
|
||||
mappingList.setBatchable();
|
||||
mappingList.setHeadered();
|
||||
mappingList.onActivate([&] { if(assignButton.enabled()) assignButton.doActivate(); });
|
||||
mappingList.onChange([&] { updateControls(); });
|
||||
assignMouse1.onActivate([&] { assignMouseInput(0); });
|
||||
|
@ -89,10 +90,8 @@ auto InputSettings::reloadDevices() -> void {
|
|||
|
||||
auto InputSettings::reloadMappings() -> void {
|
||||
mappingList.reset();
|
||||
mappingList.append(TableViewHeader().setVisible()
|
||||
.append(TableViewColumn().setText("Name"))
|
||||
.append(TableViewColumn().setText("Mapping").setExpandable())
|
||||
);
|
||||
mappingList.append(TableViewColumn().setText("Name"));
|
||||
mappingList.append(TableViewColumn().setText("Mapping").setExpandable());
|
||||
for(auto& mapping : activeDevice().mappings) {
|
||||
mappingList.append(TableViewItem()
|
||||
.append(TableViewCell().setText(mapping.name).setFont(Font().setBold()).setBackgroundColor({240, 240, 255}))
|
||||
|
|
|
@ -17,7 +17,7 @@ DriverSettings driverSettings;
|
|||
SettingsWindow settingsWindow;
|
||||
|
||||
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) {
|
||||
//create node and set to default value only if it does not already exist
|
||||
|
@ -88,7 +88,7 @@ Settings::Settings() {
|
|||
}
|
||||
|
||||
auto Settings::save() -> void {
|
||||
file::write(locate("settings.bml"), BML::serialize(*this));
|
||||
file::write(locate("settings.bml"), BML::serialize(*this, " "));
|
||||
}
|
||||
|
||||
auto SettingsWindow::create() -> void {
|
||||
|
@ -123,13 +123,14 @@ auto SettingsWindow::setVisible(bool visible) -> SettingsWindow& {
|
|||
if(visible) {
|
||||
inputSettings.refreshMappings();
|
||||
hotkeySettings.refreshMappings();
|
||||
Application::processEvents();
|
||||
doSize();
|
||||
}
|
||||
return Window::setVisible(visible), *this;
|
||||
}
|
||||
|
||||
auto SettingsWindow::show(uint index) -> void {
|
||||
panel.item(index)->setSelected();
|
||||
panel.item(index).setSelected();
|
||||
setVisible();
|
||||
setFocused();
|
||||
doSize();
|
||||
}
|
||||
|
|
|
@ -155,7 +155,7 @@ public:
|
|||
CheckLabel autoSaveStateOnUnload{&autoStateLayout, Size{0, 0}};
|
||||
CheckLabel autoLoadStateOnLoad{&autoStateLayout, 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};
|
||||
HorizontalLayout fastPPULayout{&layout, Size{~0, 0}};
|
||||
CheckLabel fastPPUOption{&fastPPULayout, Size{0, 0}};
|
||||
|
@ -202,7 +202,7 @@ public:
|
|||
CheckLabel videoExclusiveToggle{&videoToggleLayout, Size{0, 0}};
|
||||
CheckLabel videoBlockingToggle{&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};
|
||||
TableLayout audioLayout{&layout, Size{~0, 0}};
|
||||
Label audioDriverLabel{&audioLayout, Size{0, 0}};
|
||||
|
@ -221,7 +221,7 @@ public:
|
|||
CheckLabel audioExclusiveToggle{&audioToggleLayout, Size{0, 0}};
|
||||
CheckLabel audioBlockingToggle{&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};
|
||||
TableLayout inputLayout{&layout, Size{~0, 0}};
|
||||
Label inputDriverLabel{&inputLayout, Size{0, 0}};
|
||||
|
|
|
@ -55,20 +55,21 @@ auto CheatWindow::create() -> void {
|
|||
nameLabel.setText("Name:");
|
||||
nameValue.onActivate([&] { if(acceptButton.enabled()) acceptButton.doActivate(); });
|
||||
nameValue.onChange([&] { doChange(); });
|
||||
codeLayout.setAlignment(0.0);
|
||||
codeLabel.setText("Code:");
|
||||
codeValue.onActivate([&] { if(acceptButton.enabled()) acceptButton.doActivate(); });
|
||||
codeValue.setFont(Font().setFamily(Font::Mono));
|
||||
codeValue.onChange([&] { doChange(); });
|
||||
enableOption.setText("Enable");
|
||||
acceptButton.onActivate([&] { doAccept(); });
|
||||
cancelButton.setText("Cancel").onActivate([&] { setVisible(false); });
|
||||
|
||||
setSize({400, layout.minimumSize().height()});
|
||||
setSize({400, layout.minimumSize().height() + 100});
|
||||
setDismissable();
|
||||
}
|
||||
|
||||
auto CheatWindow::show(Cheat cheat) -> void {
|
||||
nameValue.setText(cheat.name);
|
||||
codeValue.setText(cheat.code);
|
||||
codeValue.setText(cheat.code.split("+").strip().merge("\n"));
|
||||
enableOption.setChecked(cheat.enable);
|
||||
doChange();
|
||||
setTitle(!cheat.name ? "Add Cheat" : "Edit Cheat");
|
||||
|
@ -87,7 +88,7 @@ auto CheatWindow::doChange() -> 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") {
|
||||
cheatEditor.addCheat(cheat);
|
||||
} else {
|
||||
|
@ -148,10 +149,8 @@ auto CheatEditor::create() -> void {
|
|||
|
||||
auto CheatEditor::refresh() -> void {
|
||||
cheatList.reset();
|
||||
cheatList.append(TableViewHeader().setVisible(false)
|
||||
.append(TableViewColumn())
|
||||
.append(TableViewColumn().setExpandable())
|
||||
);
|
||||
cheatList.append(TableViewColumn());
|
||||
cheatList.append(TableViewColumn().setExpandable());
|
||||
for(auto& cheat : cheats) {
|
||||
cheatList.append(TableViewItem()
|
||||
.append(TableViewCell().setCheckable().setChecked(cheat.enable))
|
||||
|
|
|
@ -3,18 +3,52 @@ auto ManifestViewer::create() -> void {
|
|||
setText("Manifest Viewer");
|
||||
|
||||
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 {
|
||||
if(!emulator->loaded()) {
|
||||
manifestOption.reset();
|
||||
manifestView.setText("");
|
||||
verifiedIcon.setIcon({});
|
||||
verifiedLabel.setText("");
|
||||
return;
|
||||
}
|
||||
if(!emulator->loaded()) return;
|
||||
|
||||
manifestView.setText(emulator->manifests().merge("\n"));
|
||||
verifiedIcon.setIcon(program.verified() ? Icon::Emblem::Program : Icon::Emblem::Binary);
|
||||
verifiedLabel.setText(program.verified() ? "Verified" : "Unverified");
|
||||
auto manifests = emulator->manifests();
|
||||
auto titles = emulator->titles();
|
||||
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));
|
||||
}
|
||||
|
|
|
@ -1,62 +1,45 @@
|
|||
auto StateWindow::create() -> void {
|
||||
layout.setPadding(5);
|
||||
nameLabel.setText("Name:");
|
||||
nameValue.onActivate([&] {
|
||||
if(acceptButton.enabled()) acceptButton.doActivate();
|
||||
});
|
||||
nameValue.onChange([&] {
|
||||
doChange();
|
||||
});
|
||||
acceptButton.onActivate([&] {
|
||||
doAccept();
|
||||
});
|
||||
cancelButton.setText("Cancel").onActivate([&] {
|
||||
setVisible(false);
|
||||
});
|
||||
nameValue.onActivate([&] { if(acceptButton.enabled()) acceptButton.doActivate(); });
|
||||
nameValue.onChange([&] { doChange(); });
|
||||
acceptButton.onActivate([&] { doAccept(); });
|
||||
cancelButton.setText("Cancel").onActivate([&] { setVisible(false); });
|
||||
|
||||
setSize({400, layout.minimumSize().height()});
|
||||
setDismissable();
|
||||
}
|
||||
|
||||
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();
|
||||
setTitle(!name ? "Add State" : "Edit State");
|
||||
setTitle(!property("name") ? "Add State" : "Rename State");
|
||||
setCentered(*toolsWindow);
|
||||
setVisible();
|
||||
setFocused();
|
||||
nameValue.setFocused();
|
||||
acceptButton.setText(!name ? "Add" : "Edit");
|
||||
acceptButton.setText(!property("name") ? "Add" : "Rename");
|
||||
}
|
||||
|
||||
auto StateWindow::doChange() -> void {
|
||||
bool valid = true;
|
||||
auto name = nameValue.text().strip();
|
||||
if(!name) valid = false;
|
||||
for(auto c : name) {
|
||||
if(c == '\\'
|
||||
|| c == '\"'
|
||||
|| 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;
|
||||
bool valid = name && !name.contains("\\\"\t/:*?<>|");
|
||||
if(property("name") && name != property("name")) {
|
||||
//don't allow a state to be renamed to the same name as an existing state (as this would overwrite it)
|
||||
if(program.hasState({property("type"), name})) valid = false;
|
||||
}
|
||||
nameValue.setBackgroundColor(valid ? Color{} : Color{255, 224, 224});
|
||||
acceptButton.setEnabled(valid);
|
||||
}
|
||||
|
||||
auto StateWindow::doAccept() -> void {
|
||||
string name = {property("type"), nameValue.text().strip()};
|
||||
if(acceptButton.text() == "Add") {
|
||||
stateManager.createState(nameValue.text());
|
||||
stateManager.createState(name);
|
||||
} else {
|
||||
stateManager.modifyState(nameValue.text());
|
||||
stateManager.modifyState(name);
|
||||
}
|
||||
setVisible(false);
|
||||
}
|
||||
|
@ -66,76 +49,76 @@ auto StateManager::create() -> void {
|
|||
setText("State Manager");
|
||||
|
||||
layout.setPadding(5);
|
||||
stateLayout.setAlignment(0.0);
|
||||
stateList.setBatchable();
|
||||
stateList.onActivate([&] {
|
||||
editButton.doActivate();
|
||||
});
|
||||
stateList.onChange([&] {
|
||||
auto batched = stateList.batched();
|
||||
loadButton.setEnabled(batched.size() == 1);
|
||||
saveButton.setEnabled(batched.size() == 1);
|
||||
editButton.setEnabled(batched.size() == 1);
|
||||
removeButton.setEnabled(batched.size() >= 1);
|
||||
stateList.setHeadered();
|
||||
stateList.setSortable();
|
||||
stateList.onActivate([&] { loadButton.doActivate(); });
|
||||
stateList.onChange([&] { updateSelection(); });
|
||||
stateList.onSort([&](TableViewColumn column) {
|
||||
column.setSorting(column.sorting() == Sort::Ascending ? Sort::Descending : Sort::Ascending);
|
||||
stateList.sort();
|
||||
});
|
||||
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([&] {
|
||||
if(auto item = stateList.selected()) {
|
||||
string filename = {"managed/", item.cell(0).text()};
|
||||
program.loadState(filename);
|
||||
}
|
||||
if(auto item = stateList.selected()) program.loadState(item.property("name"));
|
||||
});
|
||||
saveButton.setText("Save").onActivate([&] {
|
||||
if(auto item = stateList.selected()) {
|
||||
string filename = {"managed/", item.cell(0).text()};
|
||||
program.saveState(filename);
|
||||
item.cell(1).setText(chrono::local::datetime(program.stateTimestamp(filename)));
|
||||
}
|
||||
if(auto item = stateList.selected()) program.saveState(item.property("name"));
|
||||
});
|
||||
addButton.setText("Add").onActivate([&] {
|
||||
stateWindow.show();
|
||||
stateWindow.show(type());
|
||||
});
|
||||
editButton.setText("Edit").onActivate([&] {
|
||||
if(auto item = stateList.selected()) {
|
||||
stateWindow.show(item.cell(0).text());
|
||||
}
|
||||
editButton.setText("Rename").onActivate([&] {
|
||||
if(auto item = stateList.selected()) stateWindow.show(item.property("name"));
|
||||
});
|
||||
removeButton.setText("Remove").onActivate([&] {
|
||||
removeStates();
|
||||
});
|
||||
}
|
||||
|
||||
auto StateManager::type() const -> string {
|
||||
return categoryOption.selected().property("type");
|
||||
}
|
||||
|
||||
auto StateManager::loadStates() -> void {
|
||||
stateList.reset();
|
||||
stateList.append(TableViewHeader()
|
||||
.append(TableViewColumn().setText("Name").setExpandable())
|
||||
.append(TableViewColumn().setText("Date").setForegroundColor({160, 160, 160}))
|
||||
);
|
||||
for(auto& filename : program.availableStates("managed/")) {
|
||||
stateList.append(TableViewItem()
|
||||
.append(TableViewCell().setText(string{filename}.trimLeft("managed/", 1L)))
|
||||
.append(TableViewCell().setText(chrono::local::datetime(program.stateTimestamp(filename))))
|
||||
);
|
||||
stateList.append(TableViewColumn().setText("Name").setSorting(Sort::Ascending).setExpandable());
|
||||
stateList.append(TableViewColumn().setText("Date").setForegroundColor({160, 160, 160}));
|
||||
auto type = this->type();
|
||||
for(auto& state : program.availableStates(type)) {
|
||||
TableViewItem item{&stateList};
|
||||
item.setProperty("name", state.name);
|
||||
item.append(TableViewCell().setText(state.name.trimLeft(type, 1L)));
|
||||
item.append(TableViewCell().setText(chrono::local::datetime(state.date)));
|
||||
}
|
||||
stateList.resizeColumns().doChange();
|
||||
}
|
||||
|
||||
auto StateManager::createState(string name) -> void {
|
||||
program.saveState({"managed/", name});
|
||||
loadStates();
|
||||
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();
|
||||
}
|
||||
|
||||
auto StateManager::modifyState(string name) -> void {
|
||||
if(auto item = stateList.selected()) {
|
||||
string from = {"managed/", item.cell(0).text()};
|
||||
string to = {"managed/", name};
|
||||
string from = item.property("name");
|
||||
string to = name;
|
||||
if(from != to) {
|
||||
program.renameState(from, to);
|
||||
loadStates();
|
||||
for(auto& item : stateList.items()) {
|
||||
if(item.cell(0).text() == name) item.setSelected();
|
||||
item.setSelected(item.property("name") == to);
|
||||
}
|
||||
stateList.doChange();
|
||||
}
|
||||
|
@ -146,10 +129,43 @@ auto StateManager::removeStates() -> void {
|
|||
if(auto batched = stateList.batched()) {
|
||||
if(MessageDialog("Are you sure you want to permanently remove the selected state(s)?")
|
||||
.setParent(*toolsWindow).question() == "Yes") {
|
||||
for(auto& item : batched) {
|
||||
program.removeState({"managed/", item.cell(0).text()});
|
||||
}
|
||||
auto lock = acquire();
|
||||
for(auto& item : batched) program.removeState(item.property("name"));
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -15,9 +15,14 @@ auto ToolsWindow::create() -> void {
|
|||
panel.append(cheatEditor);
|
||||
panel.append(stateManager);
|
||||
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");
|
||||
setSize({600, 400});
|
||||
setSize({720, 480});
|
||||
setAlignment({1.0, 1.0});
|
||||
setDismissable();
|
||||
|
||||
|
@ -37,6 +42,9 @@ auto ToolsWindow::setVisible(bool visible) -> ToolsWindow& {
|
|||
cheatDatabase.setVisible(false);
|
||||
cheatWindow.setVisible(false);
|
||||
stateWindow.setVisible(false);
|
||||
} else {
|
||||
Application::processEvents();
|
||||
doSize();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
@ -45,5 +53,4 @@ auto ToolsWindow::show(uint index) -> void {
|
|||
panel.item(index).setSelected();
|
||||
setVisible();
|
||||
setFocused();
|
||||
doSize();
|
||||
}
|
||||
|
|
|
@ -38,9 +38,9 @@ public:
|
|||
HorizontalLayout nameLayout{&layout, Size{~0, 0}};
|
||||
Label nameLabel{&nameLayout, Size{40, 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}};
|
||||
LineEdit codeValue{&codeLayout, Size{~0, 0}};
|
||||
TextEdit codeValue{&codeLayout, Size{~0, ~0}};
|
||||
HorizontalLayout controlLayout{&layout, Size{~0, 0}};
|
||||
Widget spacer{&controlLayout, Size{40, 0}};
|
||||
CheckLabel enableOption{&controlLayout, Size{~0, 0}};
|
||||
|
@ -89,16 +89,34 @@ public:
|
|||
Button cancelButton{&controlLayout, Size{80, 0}};
|
||||
};
|
||||
|
||||
struct StateManager : TabFrameItem {
|
||||
struct StateManager : TabFrameItem, Lock {
|
||||
auto create() -> void;
|
||||
auto type() const -> string;
|
||||
auto loadStates() -> void;
|
||||
auto createState(string name) -> void;
|
||||
auto modifyState(string name) -> void;
|
||||
auto removeStates() -> void;
|
||||
auto updateSelection() -> void;
|
||||
auto stateEvent(string name) -> void;
|
||||
|
||||
public:
|
||||
enum class SortBy : uint {
|
||||
NameAscending,
|
||||
NameDescending,
|
||||
DateAscending,
|
||||
DateDescending,
|
||||
} sortBy = SortBy::NameAscending;
|
||||
|
||||
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}};
|
||||
Button loadButton{&controlLayout, Size{80, 0}};
|
||||
Button saveButton{&controlLayout, Size{80, 0}};
|
||||
|
@ -111,13 +129,22 @@ public:
|
|||
struct ManifestViewer : TabFrameItem {
|
||||
auto create() -> void;
|
||||
auto loadManifest() -> void;
|
||||
auto selectManifest() -> void;
|
||||
|
||||
public:
|
||||
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}};
|
||||
HorizontalLayout verifiedLayout{&layout, Size{~0, 0}};
|
||||
Canvas verifiedIcon{&verifiedLayout, Size{16, 16}};
|
||||
Label verifiedLabel{&verifiedLayout, Size{~0, 0}};
|
||||
#endif
|
||||
};
|
||||
|
||||
struct ToolsWindow : Window {
|
||||
|
|
|
@ -26,6 +26,9 @@ using namespace nall;
|
|||
#define signal(function, ...) \
|
||||
(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 {
|
||||
#include "color.cpp"
|
||||
#include "gradient.cpp"
|
||||
|
@ -90,7 +93,6 @@ namespace hiro {
|
|||
#include "widget/tab-frame.cpp"
|
||||
#include "widget/tab-frame-item.cpp"
|
||||
#include "widget/table-view.cpp"
|
||||
#include "widget/table-view-header.cpp"
|
||||
#include "widget/table-view-column.cpp"
|
||||
#include "widget/table-view-item.cpp"
|
||||
#include "widget/table-view-cell.cpp"
|
||||
|
@ -103,3 +105,4 @@ namespace hiro {
|
|||
}
|
||||
|
||||
#undef signal
|
||||
#undef signalex
|
||||
|
|
|
@ -25,6 +25,7 @@ using nall::set;
|
|||
using nall::shared_pointer;
|
||||
using nall::shared_pointer_weak;
|
||||
using nall::string;
|
||||
using nall::string_pascal;
|
||||
using nall::vector;
|
||||
|
||||
namespace hiro {
|
||||
|
@ -95,6 +96,7 @@ Declare(Viewport)
|
|||
|
||||
enum class Orientation : uint { Horizontal, Vertical };
|
||||
enum class Navigation : uint { Top, Bottom, Left, Right };
|
||||
enum class Sort : uint { None, Ascending, Descending };
|
||||
|
||||
#if defined(Hiro_Color)
|
||||
struct Color {
|
||||
|
@ -305,40 +307,7 @@ struct Geometry {
|
|||
};
|
||||
#endif
|
||||
|
||||
#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:
|
||||
struct State {
|
||||
string family;
|
||||
float size;
|
||||
bool bold;
|
||||
bool italic;
|
||||
} state;
|
||||
};
|
||||
#endif
|
||||
#include "font.hpp"
|
||||
|
||||
#if defined(Hiro_Hotkey)
|
||||
struct Hotkey {
|
||||
|
@ -707,35 +676,9 @@ struct mMenuRadioItem : mAction {
|
|||
};
|
||||
#endif
|
||||
|
||||
#if defined(Hiro_Sizable)
|
||||
struct mSizable : mObject {
|
||||
Declare(Sizable)
|
||||
#include "sizable.hpp"
|
||||
|
||||
auto geometry() const -> Geometry;
|
||||
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
|
||||
#include "widget/widget.hpp"
|
||||
|
||||
#if defined(Hiro_Button)
|
||||
struct mButton : mWidget {
|
||||
|
@ -1274,28 +1217,7 @@ struct mRadioLabel : mWidget {
|
|||
};
|
||||
#endif
|
||||
|
||||
#if defined(Hiro_SourceEdit)
|
||||
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
|
||||
#include "widget/source-edit.hpp"
|
||||
|
||||
#if defined(Hiro_TabFrame)
|
||||
struct mTabFrame : mWidget {
|
||||
|
@ -1374,210 +1296,10 @@ struct mTabFrameItem : mObject {
|
|||
};
|
||||
#endif
|
||||
|
||||
#if defined(Hiro_TableView)
|
||||
struct mTableView : mWidget {
|
||||
Declare(TableView)
|
||||
using mObject::remove;
|
||||
|
||||
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
|
||||
#include "widget/table-view.hpp"
|
||||
#include "widget/table-view-column.hpp"
|
||||
#include "widget/table-view-item.hpp"
|
||||
#include "widget/table-view-cell.hpp"
|
||||
|
||||
#if defined(Hiro_TextEdit)
|
||||
struct mTextEdit : mWidget {
|
||||
|
|
|
@ -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
|
|
@ -50,3 +50,5 @@ struct mLock {
|
|||
|
||||
mutable int locks = 0;
|
||||
};
|
||||
|
||||
using Lock = mLock;
|
||||
|
|
|
@ -199,16 +199,6 @@ auto mObject::parentTableView(bool recursive) const -> mTableView* {
|
|||
}
|
||||
#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)
|
||||
auto mObject::parentTableViewItem(bool recursive) const -> mTableViewItem* {
|
||||
if(auto tableViewItem = dynamic_cast<mTableViewItem*>(parent())) return tableViewItem;
|
||||
|
|
|
@ -28,7 +28,6 @@ struct mObject {
|
|||
auto parentTabFrame(bool recursive = false) const -> mTabFrame*;
|
||||
auto parentTabFrameItem(bool recursive = false) const -> mTabFrameItem*;
|
||||
auto parentTableView(bool recursive = false) const -> mTableView*;
|
||||
auto parentTableViewHeader(bool recursive = false) const -> mTableViewHeader*;
|
||||
auto parentTableViewItem(bool recursive = false) const -> mTableViewItem*;
|
||||
auto parentTreeView(bool recursive = false) const -> mTreeView*;
|
||||
auto parentTreeViewItem(bool recursive = false) const -> mTreeViewItem*;
|
||||
|
@ -47,17 +46,19 @@ struct mObject {
|
|||
auto visible(bool recursive = false) const -> bool;
|
||||
|
||||
//private:
|
||||
//sizeof(mObject) == 72
|
||||
struct State {
|
||||
bool enabled = true;
|
||||
Font font;
|
||||
int offset = -1;
|
||||
mObject* parent = nullptr;
|
||||
set<Property> properties;
|
||||
bool visible = true;
|
||||
Font font; //16
|
||||
set<Property> properties; //16
|
||||
mObject* parent = nullptr; // 8
|
||||
int offset = -1; // 4
|
||||
char enabled = true; // 1+
|
||||
char visible = true; // 1=4
|
||||
} state;
|
||||
|
||||
wObject instance;
|
||||
pObject* delegate = nullptr;
|
||||
wObject instance; // 8
|
||||
pObject* delegate = nullptr; // 8
|
||||
//vtable // 8
|
||||
|
||||
virtual auto construct() -> void;
|
||||
virtual auto destruct() -> void;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#define DeclareShared(Name) \
|
||||
using type = Name; \
|
||||
using internalType = m##Name; \
|
||||
Name() : s##Name(new m##Name, [](auto p) { \
|
||||
p->unbind(); \
|
||||
delete p; \
|
||||
|
@ -7,6 +8,9 @@
|
|||
(*this)->bind(*this); \
|
||||
} \
|
||||
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(); } \
|
||||
auto self() const -> m##Name& { return (m##Name&)operator*(); } \
|
||||
|
||||
|
@ -15,6 +19,9 @@
|
|||
template<typename T, typename... P> Name(T* parent, P&&... p) : Name() { \
|
||||
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 { \
|
||||
if(auto pointer = dynamic_cast<typename T::internalType*>(data())) { \
|
||||
if(auto shared = pointer->instance.acquire()) return T(shared); \
|
||||
|
@ -57,14 +64,12 @@
|
|||
#if defined(Hiro_Object)
|
||||
struct Object : sObject {
|
||||
DeclareSharedObject(Object)
|
||||
using internalType = mObject;
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(Hiro_Group)
|
||||
struct Group : sGroup {
|
||||
DeclareShared(Group)
|
||||
using internalType = mGroup;
|
||||
template<typename... P> Group(P&&... p) : Group() { _append(std::forward<P>(p)...); }
|
||||
|
||||
auto append(sObject object) -> type& { return self().append(object), *this; }
|
||||
|
@ -93,7 +98,6 @@ private:
|
|||
#if defined(Hiro_Timer)
|
||||
struct Timer : sTimer {
|
||||
DeclareSharedObject(Timer)
|
||||
using internalType = mTimer;
|
||||
|
||||
auto doActivate() const { return self().doActivate(); }
|
||||
auto interval() const { return self().interval(); }
|
||||
|
@ -105,14 +109,12 @@ struct Timer : sTimer {
|
|||
#if defined(Hiro_Action)
|
||||
struct Action : sAction {
|
||||
DeclareSharedAction(Action)
|
||||
using internalType = mAction;
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(Hiro_Menu)
|
||||
struct Menu : sMenu {
|
||||
DeclareSharedAction(Menu)
|
||||
using internalType = mMenu;
|
||||
|
||||
auto action(unsigned position) const { return self().action(position); }
|
||||
auto actionCount() const { return self().actionCount(); }
|
||||
|
@ -130,14 +132,12 @@ struct Menu : sMenu {
|
|||
#if defined(Hiro_MenuSeparator)
|
||||
struct MenuSeparator : sMenuSeparator {
|
||||
DeclareSharedAction(MenuSeparator)
|
||||
using internalType = mMenuSeparator;
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(Hiro_MenuItem)
|
||||
struct MenuItem : sMenuItem {
|
||||
DeclareSharedAction(MenuItem)
|
||||
using internalType = mMenuItem;
|
||||
|
||||
auto doActivate() const { return self().doActivate(); }
|
||||
auto icon() const { return self().icon(); }
|
||||
|
@ -151,7 +151,6 @@ struct MenuItem : sMenuItem {
|
|||
#if defined(Hiro_MenuCheckItem)
|
||||
struct MenuCheckItem : sMenuCheckItem {
|
||||
DeclareSharedAction(MenuCheckItem)
|
||||
using internalType = mMenuCheckItem;
|
||||
|
||||
auto checked() const { return self().checked(); }
|
||||
auto doToggle() const { return self().doToggle(); }
|
||||
|
@ -165,7 +164,6 @@ struct MenuCheckItem : sMenuCheckItem {
|
|||
#if defined(Hiro_MenuRadioItem)
|
||||
struct MenuRadioItem : sMenuRadioItem {
|
||||
DeclareSharedAction(MenuRadioItem)
|
||||
using internalType = mMenuRadioItem;
|
||||
|
||||
auto checked() const { return self().checked(); }
|
||||
auto doActivate() const { return self().doActivate(); }
|
||||
|
@ -180,21 +178,18 @@ struct MenuRadioItem : sMenuRadioItem {
|
|||
#if defined(Hiro_Sizable)
|
||||
struct Sizable : sSizable {
|
||||
DeclareSharedSizable(Sizable)
|
||||
using internalType = mSizable;
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(Hiro_Widget)
|
||||
struct Widget : sWidget {
|
||||
DeclareSharedWidget(Widget)
|
||||
using internalType = mWidget;
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(Hiro_Button)
|
||||
struct Button : sButton {
|
||||
DeclareSharedWidget(Button)
|
||||
using internalType = mButton;
|
||||
|
||||
auto bordered() const { return self().bordered(); }
|
||||
auto doActivate() const { return self().doActivate(); }
|
||||
|
@ -212,7 +207,6 @@ struct Button : sButton {
|
|||
#if defined(Hiro_Canvas)
|
||||
struct Canvas : sCanvas {
|
||||
DeclareSharedWidget(Canvas)
|
||||
using internalType = mCanvas;
|
||||
|
||||
auto color() const { return self().color(); }
|
||||
auto data() { return self().data(); }
|
||||
|
@ -241,7 +235,6 @@ struct Canvas : sCanvas {
|
|||
#if defined(Hiro_CheckButton)
|
||||
struct CheckButton : sCheckButton {
|
||||
DeclareSharedWidget(CheckButton)
|
||||
using internalType = mCheckButton;
|
||||
|
||||
auto bordered() const { return self().bordered(); }
|
||||
auto checked() const { return self().checked(); }
|
||||
|
@ -261,7 +254,6 @@ struct CheckButton : sCheckButton {
|
|||
#if defined(Hiro_CheckLabel)
|
||||
struct CheckLabel : sCheckLabel {
|
||||
DeclareSharedWidget(CheckLabel)
|
||||
using internalType = mCheckLabel;
|
||||
|
||||
auto checked() const { return self().checked(); }
|
||||
auto doToggle() const { return self().doToggle(); }
|
||||
|
@ -275,7 +267,6 @@ struct CheckLabel : sCheckLabel {
|
|||
#if defined(Hiro_ComboButton)
|
||||
struct ComboButtonItem : sComboButtonItem {
|
||||
DeclareSharedObject(ComboButtonItem)
|
||||
using internalType = mComboButtonItem;
|
||||
|
||||
auto icon() const { return self().icon(); }
|
||||
auto selected() const { return self().selected(); }
|
||||
|
@ -289,7 +280,6 @@ struct ComboButtonItem : sComboButtonItem {
|
|||
#if defined(Hiro_ComboButton)
|
||||
struct ComboButton : sComboButton {
|
||||
DeclareSharedWidget(ComboButton)
|
||||
using internalType = mComboButton;
|
||||
|
||||
auto append(sComboButtonItem item) { return self().append(item), *this; }
|
||||
auto doChange() const { return self().doChange(); }
|
||||
|
@ -307,7 +297,6 @@ struct ComboButton : sComboButton {
|
|||
#if defined(Hiro_ComboEdit)
|
||||
struct ComboEditItem : sComboEditItem {
|
||||
DeclareSharedObject(ComboEditItem)
|
||||
using internalType = mComboEditItem;
|
||||
|
||||
auto icon() const { return self().icon(); }
|
||||
auto setIcon(const image& icon = {}) { return self().setIcon(icon), *this; }
|
||||
|
@ -319,7 +308,6 @@ struct ComboEditItem : sComboEditItem {
|
|||
#if defined(Hiro_ComboEdit)
|
||||
struct ComboEdit : sComboEdit {
|
||||
DeclareSharedWidget(ComboEdit)
|
||||
using internalType = mComboEdit;
|
||||
|
||||
auto append(sComboEditItem item) { return self().append(item), *this; }
|
||||
auto backgroundColor() const { return self().backgroundColor(); }
|
||||
|
@ -345,7 +333,6 @@ struct ComboEdit : sComboEdit {
|
|||
#if defined(Hiro_Console)
|
||||
struct Console : sConsole {
|
||||
DeclareSharedWidget(Console)
|
||||
using internalType = mConsole;
|
||||
|
||||
auto backgroundColor() const { return self().backgroundColor(); }
|
||||
auto doActivate(string command) const { return self().doActivate(command); }
|
||||
|
@ -363,7 +350,6 @@ struct Console : sConsole {
|
|||
#if defined(Hiro_Frame)
|
||||
struct Frame : sFrame {
|
||||
DeclareSharedWidget(Frame)
|
||||
using internalType = mFrame;
|
||||
|
||||
auto append(sSizable sizable) { return self().append(sizable), *this; }
|
||||
auto remove(sSizable sizable) { return self().remove(sizable), *this; }
|
||||
|
@ -377,7 +363,6 @@ struct Frame : sFrame {
|
|||
#if defined(Hiro_HexEdit)
|
||||
struct HexEdit : sHexEdit {
|
||||
DeclareSharedWidget(HexEdit)
|
||||
using internalType = mHexEdit;
|
||||
|
||||
auto address() const { return self().address(); }
|
||||
auto backgroundColor() const { return self().backgroundColor(); }
|
||||
|
@ -402,7 +387,6 @@ struct HexEdit : sHexEdit {
|
|||
#if defined(Hiro_HorizontalScrollBar)
|
||||
struct HorizontalScrollBar : sHorizontalScrollBar {
|
||||
DeclareSharedWidget(HorizontalScrollBar)
|
||||
using internalType = mHorizontalScrollBar;
|
||||
|
||||
auto doChange() const { return self().doChange(); }
|
||||
auto length() const { return self().length(); }
|
||||
|
@ -416,7 +400,6 @@ struct HorizontalScrollBar : sHorizontalScrollBar {
|
|||
#if defined(Hiro_HorizontalSlider)
|
||||
struct HorizontalSlider : sHorizontalSlider {
|
||||
DeclareSharedWidget(HorizontalSlider)
|
||||
using internalType = mHorizontalSlider;
|
||||
|
||||
auto doChange() const { return self().doChange(); }
|
||||
auto length() const { return self().length(); }
|
||||
|
@ -430,7 +413,6 @@ struct HorizontalSlider : sHorizontalSlider {
|
|||
#if defined(Hiro_IconView)
|
||||
struct IconViewItem : sIconViewItem {
|
||||
DeclareSharedObject(IconViewItem)
|
||||
using internalType = mIconViewItem;
|
||||
|
||||
auto icon() const { return self().icon(); }
|
||||
auto selected() const { return self().selected(); }
|
||||
|
@ -444,7 +426,6 @@ struct IconViewItem : sIconViewItem {
|
|||
#if defined(Hiro_IconView)
|
||||
struct IconView : sIconView {
|
||||
DeclareSharedWidget(IconView)
|
||||
using internalType = mIconView;
|
||||
|
||||
auto append(sIconViewItem item) { return self().append(item), *this; }
|
||||
auto backgroundColor() const { return self().backgroundColor(); }
|
||||
|
@ -477,7 +458,6 @@ struct IconView : sIconView {
|
|||
#if defined(Hiro_Label)
|
||||
struct Label : sLabel {
|
||||
DeclareSharedWidget(Label)
|
||||
using internalType = mLabel;
|
||||
|
||||
auto alignment() const { return self().alignment(); }
|
||||
auto backgroundColor() const { return self().backgroundColor(); }
|
||||
|
@ -493,7 +473,6 @@ struct Label : sLabel {
|
|||
#if defined(Hiro_LineEdit)
|
||||
struct LineEdit : sLineEdit {
|
||||
DeclareSharedWidget(LineEdit)
|
||||
using internalType = mLineEdit;
|
||||
|
||||
auto backgroundColor() const { return self().backgroundColor(); }
|
||||
auto doActivate() const { return self().doActivate(); }
|
||||
|
@ -513,7 +492,6 @@ struct LineEdit : sLineEdit {
|
|||
#if defined(Hiro_ProgressBar)
|
||||
struct ProgressBar : sProgressBar {
|
||||
DeclareSharedWidget(ProgressBar)
|
||||
using internalType = mProgressBar;
|
||||
|
||||
auto position() const { return self().position(); }
|
||||
auto setPosition(unsigned position = 0) { return self().setPosition(position), *this; }
|
||||
|
@ -523,7 +501,6 @@ struct ProgressBar : sProgressBar {
|
|||
#if defined(Hiro_RadioButton)
|
||||
struct RadioButton : sRadioButton {
|
||||
DeclareSharedWidget(RadioButton)
|
||||
using internalType = mRadioButton;
|
||||
|
||||
auto bordered() const { return self().bordered(); }
|
||||
auto checked() const { return self().checked(); }
|
||||
|
@ -544,7 +521,6 @@ struct RadioButton : sRadioButton {
|
|||
#if defined(Hiro_RadioLabel)
|
||||
struct RadioLabel : sRadioLabel {
|
||||
DeclareSharedWidget(RadioLabel)
|
||||
using internalType = mRadioLabel;
|
||||
|
||||
auto checked() const { return self().checked(); }
|
||||
auto doActivate() const { return self().doActivate(); }
|
||||
|
@ -559,23 +535,25 @@ struct RadioLabel : sRadioLabel {
|
|||
#if defined(Hiro_SourceEdit)
|
||||
struct SourceEdit : sSourceEdit {
|
||||
DeclareSharedWidget(SourceEdit)
|
||||
using internalType = mSourceEdit;
|
||||
|
||||
auto cursor() const { return self().cursor(); }
|
||||
auto doChange() const { return self().doChange(); }
|
||||
auto doMove() const { return self().doMove(); }
|
||||
auto editable() const { return self().editable(); }
|
||||
auto onChange(const function<void ()>& callback = {}) { return self().onChange(callback), *this; }
|
||||
auto onMove(const function<void ()>& callback = {}) { return self().onMove(callback), *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 setWordWrap(bool wordWrap = true) { return self().setWordWrap(wordWrap), *this; }
|
||||
auto text() const { return self().text(); }
|
||||
auto wordWrap() const { return self().wordWrap(); }
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(Hiro_TabFrame)
|
||||
struct TabFrameItem : sTabFrameItem {
|
||||
DeclareSharedObject(TabFrameItem)
|
||||
using internalType = mTabFrameItem;
|
||||
|
||||
auto append(sSizable sizable) { return self().append(sizable), *this; }
|
||||
auto closable() const { return self().closable(); }
|
||||
|
@ -597,7 +575,6 @@ struct TabFrameItem : sTabFrameItem {
|
|||
#if defined(Hiro_TabFrame)
|
||||
struct TabFrame : sTabFrame {
|
||||
DeclareSharedWidget(TabFrame)
|
||||
using internalType = mTabFrame;
|
||||
|
||||
auto append(sTabFrameItem item) { return self().append(item), *this; }
|
||||
auto doChange() const { return self().doChange(); }
|
||||
|
@ -620,7 +597,6 @@ struct TabFrame : sTabFrame {
|
|||
#if defined(Hiro_TableView)
|
||||
struct TableViewColumn : sTableViewColumn {
|
||||
DeclareSharedObject(TableViewColumn)
|
||||
using internalType = mTableViewColumn;
|
||||
|
||||
auto active() const { return self().active(); }
|
||||
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 setIcon(const image& icon = {}) { return self().setIcon(icon), *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 setVerticalAlignment(float alignment = 0.5) { return self().setVerticalAlignment(alignment), *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 verticalAlignment() const { return self().verticalAlignment(); }
|
||||
auto width() const { return self().width(); }
|
||||
};
|
||||
#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)
|
||||
struct TableViewCell : sTableViewCell {
|
||||
DeclareSharedObject(TableViewCell)
|
||||
using internalType = mTableViewCell;
|
||||
|
||||
auto alignment() const { return self().alignment(); }
|
||||
auto backgroundColor() const { return self().backgroundColor(); }
|
||||
|
@ -690,7 +652,6 @@ struct TableViewCell : sTableViewCell {
|
|||
#if defined(Hiro_TableView)
|
||||
struct TableViewItem : sTableViewItem {
|
||||
DeclareSharedObject(TableViewItem)
|
||||
using internalType = mTableViewItem;
|
||||
|
||||
auto alignment() const { return self().alignment(); }
|
||||
auto append(sTableViewCell cell) { return self().append(cell), *this; }
|
||||
|
@ -712,15 +673,17 @@ struct TableViewItem : sTableViewItem {
|
|||
#if defined(Hiro_TableView)
|
||||
struct TableView : sTableView {
|
||||
DeclareSharedWidget(TableView)
|
||||
using internalType = mTableView;
|
||||
|
||||
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 backgroundColor() const { return self().backgroundColor(); }
|
||||
auto batchable() const { return self().batchable(); }
|
||||
auto batched() const { return self().batched(); }
|
||||
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 doChange() const { return self().doChange(); }
|
||||
auto doContext() const { return self().doContext(); }
|
||||
|
@ -728,7 +691,7 @@ struct TableView : sTableView {
|
|||
auto doSort(sTableViewColumn column) const { return self().doSort(column); }
|
||||
auto doToggle(sTableViewCell cell) const { return self().doToggle(cell); }
|
||||
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 itemCount() const { return self().itemCount(); }
|
||||
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 onSort(const function<void (TableViewColumn)>& callback = {}) { return self().onSort(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 reset() { return self().reset(), *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 setBordered(bool bordered = true) { return self().setBordered(bordered), *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
|
||||
|
||||
#if defined(Hiro_TextEdit)
|
||||
struct TextEdit : sTextEdit {
|
||||
DeclareSharedWidget(TextEdit)
|
||||
using internalType = mTextEdit;
|
||||
|
||||
auto backgroundColor() const { return self().backgroundColor(); }
|
||||
auto cursor() const { return self().cursor(); }
|
||||
|
@ -778,7 +744,6 @@ struct TextEdit : sTextEdit {
|
|||
#if defined(Hiro_TreeView)
|
||||
struct TreeViewItem : sTreeViewItem {
|
||||
DeclareSharedObject(TreeViewItem)
|
||||
using internalType = mTreeViewItem;
|
||||
|
||||
auto append(sTreeViewItem item) { return self().append(item), *this; }
|
||||
auto backgroundColor() const { return self().backgroundColor(); }
|
||||
|
@ -807,7 +772,6 @@ struct TreeViewItem : sTreeViewItem {
|
|||
#if defined(Hiro_TreeView)
|
||||
struct TreeView : sTreeView {
|
||||
DeclareSharedWidget(TreeView)
|
||||
using internalType = mTreeView;
|
||||
|
||||
auto append(sTreeViewItem item) { return self().append(item), *this; }
|
||||
auto backgroundColor() const { return self().backgroundColor(); }
|
||||
|
@ -834,7 +798,6 @@ struct TreeView : sTreeView {
|
|||
#if defined(Hiro_VerticalScrollBar)
|
||||
struct VerticalScrollBar : sVerticalScrollBar {
|
||||
DeclareSharedWidget(VerticalScrollBar)
|
||||
using internalType = mVerticalScrollBar;
|
||||
|
||||
auto doChange() const { return self().doChange(); }
|
||||
auto length() const { return self().length(); }
|
||||
|
@ -848,7 +811,6 @@ struct VerticalScrollBar : sVerticalScrollBar {
|
|||
#if defined(Hiro_VerticalSlider)
|
||||
struct VerticalSlider : sVerticalSlider {
|
||||
DeclareSharedWidget(VerticalSlider)
|
||||
using internalType = mVerticalSlider;
|
||||
|
||||
auto doChange() const { return self().doChange(); }
|
||||
auto length() const { return self().length(); }
|
||||
|
@ -862,7 +824,6 @@ struct VerticalSlider : sVerticalSlider {
|
|||
#if defined(Hiro_Viewport)
|
||||
struct Viewport : sViewport {
|
||||
DeclareSharedWidget(Viewport)
|
||||
using internalType = mViewport;
|
||||
|
||||
auto doDrop(vector<string> names) const { return self().doDrop(names); }
|
||||
auto doMouseLeave() const { return self().doMouseLeave(); }
|
||||
|
@ -883,7 +844,6 @@ struct Viewport : sViewport {
|
|||
#if defined(Hiro_StatusBar)
|
||||
struct StatusBar : sStatusBar {
|
||||
DeclareSharedObject(StatusBar)
|
||||
using internalType = mStatusBar;
|
||||
|
||||
auto setText(const string& text = "") { return self().setText(text), *this; }
|
||||
auto text() const { return self().text(); }
|
||||
|
@ -893,7 +853,6 @@ struct StatusBar : sStatusBar {
|
|||
#if defined(Hiro_PopupMenu)
|
||||
struct PopupMenu : sPopupMenu {
|
||||
DeclareSharedObject(PopupMenu)
|
||||
using internalType = mPopupMenu;
|
||||
|
||||
auto action(unsigned position) const { return self().action(position); }
|
||||
auto actionCount() const { return self().actionCount(); }
|
||||
|
@ -907,7 +866,6 @@ struct PopupMenu : sPopupMenu {
|
|||
#if defined(Hiro_MenuBar)
|
||||
struct MenuBar : sMenuBar {
|
||||
DeclareSharedObject(MenuBar)
|
||||
using internalType = mMenuBar;
|
||||
|
||||
auto append(sMenu menu) { return self().append(menu), *this; }
|
||||
auto menu(unsigned position) const { return self().menu(position); }
|
||||
|
@ -921,7 +879,6 @@ struct MenuBar : sMenuBar {
|
|||
#if defined(Hiro_Window)
|
||||
struct Window : sWindow {
|
||||
DeclareSharedObject(Window)
|
||||
using internalType = mWindow;
|
||||
|
||||
auto append(sMenuBar menuBar) { return self().append(menuBar), *this; }
|
||||
auto append(sSizable sizable) { return self().append(sizable), *this; }
|
||||
|
|
|
@ -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
|
|
@ -73,6 +73,8 @@ auto mCanvas::onMouseRelease(const function<void (Mouse::Button)>& callback) ->
|
|||
|
||||
auto mCanvas::setColor(Color color) -> type& {
|
||||
state.color = color;
|
||||
state.gradient = {};
|
||||
state.icon = {};
|
||||
signal(setColor, color);
|
||||
return *this;
|
||||
}
|
||||
|
@ -84,12 +86,16 @@ auto mCanvas::setDroppable(bool droppable) -> type& {
|
|||
}
|
||||
|
||||
auto mCanvas::setGradient(Gradient gradient) -> type& {
|
||||
state.color = {};
|
||||
state.gradient = gradient;
|
||||
state.icon = {};
|
||||
signal(setGradient, gradient);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto mCanvas::setIcon(const image& icon) -> type& {
|
||||
state.color = {};
|
||||
state.gradient = {};
|
||||
state.icon = icon;
|
||||
signal(setIcon, icon);
|
||||
return *this;
|
||||
|
|
|
@ -18,6 +18,10 @@ auto mSourceEdit::doMove() const -> void {
|
|||
if(state.onMove) return state.onMove();
|
||||
}
|
||||
|
||||
auto mSourceEdit::editable() const -> bool {
|
||||
return state.editable;
|
||||
}
|
||||
|
||||
auto mSourceEdit::onChange(const function<void ()>& callback) -> type& {
|
||||
state.onChange = callback;
|
||||
return *this;
|
||||
|
@ -34,14 +38,30 @@ auto mSourceEdit::setCursor(Cursor cursor) -> type& {
|
|||
return *this;
|
||||
}
|
||||
|
||||
auto mSourceEdit::setEditable(bool editable) -> type& {
|
||||
state.editable = editable;
|
||||
signal(setEditable, editable);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto mSourceEdit::setText(const string& text) -> type& {
|
||||
state.text = text;
|
||||
signal(setText, text);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto mSourceEdit::setWordWrap(bool wordWrap) -> type& {
|
||||
state.wordWrap = wordWrap;
|
||||
signal(setWordWrap, wordWrap);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto mSourceEdit::text() const -> string {
|
||||
return signal(text);
|
||||
}
|
||||
|
||||
auto mSourceEdit::wordWrap() const -> bool {
|
||||
return state.wordWrap;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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
|
|
@ -12,13 +12,11 @@ auto mTableViewCell::alignment(bool recursive) const -> Alignment {
|
|||
if(auto parent = parentTableViewItem()) {
|
||||
if(auto alignment = parent->state.alignment) return alignment;
|
||||
if(auto grandparent = parent->parentTableView()) {
|
||||
if(auto header = grandparent->state.header) {
|
||||
if(offset() < header->columnCount()) {
|
||||
if(auto column = header->state.columns[offset()]) {
|
||||
if(offset() < grandparent->columnCount()) {
|
||||
if(auto column = grandparent->state.columns[offset()]) {
|
||||
if(auto alignment = column->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 color = parent->state.backgroundColor) return color;
|
||||
if(auto grandparent = parent->parentTableView()) {
|
||||
if(auto header = grandparent->state.header) {
|
||||
if(offset() < header->columnCount()) {
|
||||
if(auto column = header->state.columns[offset()]) {
|
||||
if(offset() < grandparent->columnCount()) {
|
||||
if(auto column = grandparent->state.columns[offset()]) {
|
||||
if(auto color = column->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 font = parent->font()) return font;
|
||||
if(auto grandparent = parent->parentTableView()) {
|
||||
if(auto header = grandparent->state.header) {
|
||||
if(offset() < header->columnCount()) {
|
||||
if(auto column = header->state.columns[offset()]) {
|
||||
if(offset() < grandparent->columnCount()) {
|
||||
if(auto column = grandparent->state.columns[offset()]) {
|
||||
if(auto font = column->font()) 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 color = parent->state.foregroundColor) return color;
|
||||
if(auto grandparent = parent->parentTableView()) {
|
||||
if(auto header = grandparent->state.header) {
|
||||
if(offset() < header->columnCount()) {
|
||||
if(auto column = header->state.columns[offset()]) {
|
||||
if(offset() < grandparent->columnCount()) {
|
||||
if(auto column = grandparent->state.columns[offset()]) {
|
||||
if(auto color = column->state.foregroundColor) return color;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(auto color = grandparent->state.foregroundColor) return color;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -42,7 +42,7 @@ auto mTableViewColumn::icon() const -> image {
|
|||
}
|
||||
|
||||
auto mTableViewColumn::remove() -> type& {
|
||||
if(auto tableViewHeader = parentTableViewHeader()) tableViewHeader->remove(*this);
|
||||
if(auto tableView = parentTableView()) tableView->remove(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -105,9 +105,15 @@ auto mTableViewColumn::setResizable(bool resizable) -> type& {
|
|||
return *this;
|
||||
}
|
||||
|
||||
auto mTableViewColumn::setSortable(bool sortable) -> type& {
|
||||
state.sortable = sortable;
|
||||
signal(setSortable, sortable);
|
||||
auto mTableViewColumn::setSorting(Sort sorting) -> type& {
|
||||
if(auto tableView = parentTableView()) {
|
||||
for(auto& column : tableView->state.columns) {
|
||||
column->state.sorting = Sort::None;
|
||||
signalex(column, setSorting, Sort::None);
|
||||
}
|
||||
}
|
||||
state.sorting = sorting;
|
||||
signal(setSorting, sorting);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -136,8 +142,8 @@ auto mTableViewColumn::setWidth(float width) -> type& {
|
|||
return *this;
|
||||
}
|
||||
|
||||
auto mTableViewColumn::sortable() const -> bool {
|
||||
return state.sortable;
|
||||
auto mTableViewColumn::sorting() const -> Sort {
|
||||
return state.sorting;
|
||||
}
|
||||
|
||||
auto mTableViewColumn::text() const -> string {
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -6,7 +6,7 @@ auto mTableView::allocate() -> pObject* {
|
|||
|
||||
auto mTableView::destruct() -> void {
|
||||
for(auto& item : state.items) item->destruct();
|
||||
if(auto& header = state.header) header->destruct();
|
||||
for(auto& column : state.columns) column->destruct();
|
||||
mWidget::destruct();
|
||||
}
|
||||
|
||||
|
@ -16,11 +16,10 @@ auto mTableView::alignment() const -> Alignment {
|
|||
return state.alignment;
|
||||
}
|
||||
|
||||
auto mTableView::append(sTableViewHeader header) -> type& {
|
||||
if(auto& header = state.header) remove(header);
|
||||
state.header = header;
|
||||
header->setParent(this, 0);
|
||||
signal(append, header);
|
||||
auto mTableView::append(sTableViewColumn column) -> type& {
|
||||
state.columns.append(column);
|
||||
column->setParent(this, columnCount() - 1);
|
||||
signal(append, column);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -51,6 +50,21 @@ auto mTableView::bordered() const -> bool {
|
|||
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 {
|
||||
if(state.onActivate) return state.onActivate();
|
||||
}
|
||||
|
@ -79,8 +93,8 @@ auto mTableView::foregroundColor() const -> Color {
|
|||
return state.foregroundColor;
|
||||
}
|
||||
|
||||
auto mTableView::header() const -> TableViewHeader {
|
||||
return state.header;
|
||||
auto mTableView::headered() const -> bool {
|
||||
return state.headered;
|
||||
}
|
||||
|
||||
auto mTableView::item(unsigned position) const -> TableViewItem {
|
||||
|
@ -128,17 +142,20 @@ auto mTableView::onToggle(const function<void (TableViewCell)>& callback) -> typ
|
|||
return *this;
|
||||
}
|
||||
|
||||
auto mTableView::remove(sTableViewHeader header) -> type& {
|
||||
signal(remove, header);
|
||||
header->setParent();
|
||||
state.header.reset();
|
||||
auto mTableView::remove(sTableViewColumn column) -> type& {
|
||||
signal(remove, column);
|
||||
state.columns.remove(column->offset());
|
||||
for(uint n : range(column->offset(), columnCount())) {
|
||||
state.columns[n]->adjustOffset(-1);
|
||||
}
|
||||
column->setParent();
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto mTableView::remove(sTableViewItem item) -> type& {
|
||||
signal(remove, item);
|
||||
state.items.remove(item->offset());
|
||||
for(auto n : range(item->offset(), itemCount())) {
|
||||
for(uint n : range(item->offset(), itemCount())) {
|
||||
state.items[n]->adjustOffset(-1);
|
||||
}
|
||||
item->setParent();
|
||||
|
@ -146,8 +163,8 @@ auto mTableView::remove(sTableViewItem item) -> type& {
|
|||
}
|
||||
|
||||
auto mTableView::reset() -> type& {
|
||||
while(state.items) remove(state.items.right());
|
||||
if(auto& header = state.header) remove(header);
|
||||
while(state.items) remove(state.items.last());
|
||||
while(state.columns) remove(state.columns.last());
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -193,13 +210,51 @@ auto mTableView::setForegroundColor(Color color) -> type& {
|
|||
return *this;
|
||||
}
|
||||
|
||||
auto mTableView::setHeadered(bool headered) -> type& {
|
||||
state.headered = headered;
|
||||
signal(setHeadered, headered);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto mTableView::setParent(mObject* parent, signed offset) -> type& {
|
||||
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);
|
||||
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());
|
||||
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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -14,7 +14,7 @@ mListView::mListView() {
|
|||
}
|
||||
}
|
||||
});
|
||||
append(TableViewHeader().setVisible(false).append(TableViewColumn().setExpandable()));
|
||||
append(TableViewColumn().setExpandable());
|
||||
}
|
||||
|
||||
auto mListView::batched() const -> vector<ListViewItem> {
|
||||
|
@ -64,7 +64,7 @@ auto mListView::onToggle(const function<void (ListViewItem)>& callback) -> type&
|
|||
|
||||
auto mListView::reset() -> type& {
|
||||
mTableView::reset();
|
||||
append(TableViewHeader().setVisible(false).append(TableViewColumn().setExpandable()));
|
||||
append(TableViewColumn().setExpandable());
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
|
@ -52,7 +52,6 @@
|
|||
#include "widget/tab-frame.cpp"
|
||||
#include "widget/tab-frame-item.cpp"
|
||||
#include "widget/table-view.cpp"
|
||||
#include "widget/table-view-header.cpp"
|
||||
#include "widget/table-view-column.cpp"
|
||||
#include "widget/table-view-item.cpp"
|
||||
#include "widget/table-view-cell.cpp"
|
||||
|
|
|
@ -56,7 +56,6 @@
|
|||
#include "widget/tab-frame.hpp"
|
||||
#include "widget/tab-frame-item.hpp"
|
||||
#include "widget/table-view.hpp"
|
||||
#include "widget/table-view-header.hpp"
|
||||
#include "widget/table-view-column.hpp"
|
||||
#include "widget/table-view-item.hpp"
|
||||
#include "widget/table-view-cell.hpp"
|
||||
|
|
|
@ -54,7 +54,9 @@ auto pSourceEdit::construct() -> void {
|
|||
gtk_container_add(gtkContainer, gtkWidgetSourceView);
|
||||
gtk_widget_show(gtkWidgetSourceView);
|
||||
|
||||
setEditable(state().editable);
|
||||
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), "notify::cursor-position", G_CALLBACK(SourceEdit_move), (gpointer)this);
|
||||
|
@ -82,6 +84,10 @@ auto pSourceEdit::setCursor(Cursor cursor) -> void {
|
|||
unlock();
|
||||
}
|
||||
|
||||
auto pSourceEdit::setEditable(bool editable) -> void {
|
||||
gtk_text_view_set_editable(gtkTextView, editable);
|
||||
}
|
||||
|
||||
auto pSourceEdit::setFocused() -> void {
|
||||
gtk_widget_grab_focus(gtkWidgetSourceView);
|
||||
}
|
||||
|
@ -128,6 +134,11 @@ auto pSourceEdit::setText(const string& text) -> void {
|
|||
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 {
|
||||
GtkTextIter startIter;
|
||||
gtk_text_buffer_get_start_iter(gtkTextBuffer, &startIter);
|
||||
|
|
|
@ -6,8 +6,10 @@ struct pSourceEdit : pWidget {
|
|||
Declare(SourceEdit, Widget)
|
||||
|
||||
auto setCursor(Cursor cursor) -> void;
|
||||
auto setEditable(bool editable) -> void;
|
||||
auto setFocused() -> void override;
|
||||
auto setText(const string& text) -> void;
|
||||
auto setWordWrap(bool wordWrap) -> void;
|
||||
auto text() const -> string;
|
||||
|
||||
GtkScrolledWindow* gtkScrolledWindow = nullptr;
|
||||
|
|
|
@ -48,8 +48,7 @@ auto pTableViewCell::_parent() -> maybe<pTableViewItem&> {
|
|||
auto pTableViewCell::_setState() -> void {
|
||||
if(auto parent = _parent()) {
|
||||
if(auto grandparent = _grandparent()) {
|
||||
if(auto& tableViewHeader = grandparent->state().header) {
|
||||
if(self().offset() < tableViewHeader->columnCount()) {
|
||||
if(self().offset() < grandparent->self().columnCount()) {
|
||||
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() + 1, CreatePixbuf(state().icon), -1);
|
||||
|
@ -57,7 +56,6 @@ auto pTableViewCell::_setState() -> void {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
namespace hiro {
|
||||
|
||||
auto pTableViewColumn::construct() -> void {
|
||||
if(auto grandparent = _grandparent()) {
|
||||
auto handle = grandparent.data();
|
||||
if(auto parent = _parent()) {
|
||||
auto handle = parent.data();
|
||||
uint offset = self().offset();
|
||||
|
||||
#if HIRO_GTK==2
|
||||
|
@ -16,9 +16,12 @@ auto pTableViewColumn::construct() -> void {
|
|||
gtkHeaderIcon = gtk_image_new();
|
||||
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);
|
||||
|
||||
gtkHeaderSort = gtk_label_new("");
|
||||
gtk_box_pack_start(GTK_BOX(gtkHeader), gtkHeaderSort, false, false, 0);
|
||||
|
||||
gtkColumn = gtk_tree_view_column_new();
|
||||
gtk_tree_view_column_set_sizing(gtkColumn, GTK_TREE_VIEW_COLUMN_FIXED);
|
||||
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(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);
|
||||
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 {
|
||||
if(auto grandparent = _grandparent()) {
|
||||
gtk_tree_view_remove_column(grandparent->gtkTreeView, gtkColumn);
|
||||
if(auto parent = _parent()) {
|
||||
gtk_tree_view_remove_column(parent->gtkTreeView, gtkColumn);
|
||||
gtkColumn = nullptr;
|
||||
grandparent->_createModel();
|
||||
parent->_createModel();
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -74,8 +87,8 @@ auto pTableViewColumn::setEditable(bool editable) -> void {
|
|||
}
|
||||
|
||||
auto pTableViewColumn::setExpandable(bool expandable) -> void {
|
||||
if(auto grandparent = _grandparent()) {
|
||||
grandparent->resizeColumns();
|
||||
if(auto parent = _parent()) {
|
||||
parent->resizeColumns();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -94,47 +107,35 @@ auto pTableViewColumn::setIcon(const image& icon) -> void {
|
|||
}
|
||||
|
||||
auto pTableViewColumn::setResizable(bool resizable) -> void {
|
||||
_setState();
|
||||
gtk_tree_view_column_set_resizable(gtkColumn, resizable);
|
||||
}
|
||||
|
||||
auto pTableViewColumn::setSortable(bool sortable) -> void {
|
||||
_setState();
|
||||
auto pTableViewColumn::setSorting(Sort sorting) -> void {
|
||||
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 {
|
||||
_setState();
|
||||
gtk_label_set_text(GTK_LABEL(gtkHeaderText), text);
|
||||
}
|
||||
|
||||
auto pTableViewColumn::setVisible(bool visible) -> void {
|
||||
_setState();
|
||||
gtk_tree_view_column_set_visible(gtkColumn, visible);
|
||||
}
|
||||
|
||||
auto pTableViewColumn::setWidth(signed width) -> void {
|
||||
if(auto grandparent = _grandparent()) {
|
||||
grandparent->resizeColumns();
|
||||
if(auto parent = _parent()) {
|
||||
parent->resizeColumns();
|
||||
}
|
||||
}
|
||||
|
||||
auto pTableViewColumn::_grandparent() -> maybe<pTableView&> {
|
||||
if(auto parent = _parent()) return parent->_parent();
|
||||
return nothing;
|
||||
}
|
||||
|
||||
auto pTableViewColumn::_parent() -> maybe<pTableViewHeader&> {
|
||||
if(auto parent = self().parentTableViewHeader()) {
|
||||
auto pTableViewColumn::_parent() -> maybe<pTableView&> {
|
||||
if(auto parent = self().parentTableView()) {
|
||||
if(auto self = parent->self()) return *self;
|
||||
}
|
||||
return nothing;
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -15,20 +15,19 @@ struct pTableViewColumn : pObject {
|
|||
auto setHorizontalAlignment(double) -> void {}
|
||||
auto setIcon(const image& icon) -> void;
|
||||
auto setResizable(bool resizable) -> void;
|
||||
auto setSortable(bool sortable) -> void;
|
||||
auto setSorting(Sort sorting) -> void;
|
||||
auto setText(const string& text) -> void;
|
||||
auto setVerticalAlignment(double) -> void {}
|
||||
auto setVisible(bool visible) -> void override;
|
||||
auto setWidth(signed width) -> void;
|
||||
|
||||
auto _grandparent() -> maybe<pTableView&>;
|
||||
auto _parent() -> maybe<pTableViewHeader&>;
|
||||
auto _setState() -> void;
|
||||
auto _parent() -> maybe<pTableView&>;
|
||||
|
||||
GtkTreeViewColumn* gtkColumn = nullptr;
|
||||
GtkWidget* gtkHeader = nullptr;
|
||||
GtkWidget* gtkHeaderIcon = nullptr;
|
||||
GtkWidget* gtkHeaderText = nullptr;
|
||||
GtkWidget* gtkHeaderSort = nullptr;
|
||||
GtkCellRenderer* gtkCellToggle = nullptr;
|
||||
GtkCellRenderer* gtkCellIcon = nullptr;
|
||||
GtkCellRenderer* gtkCellText = nullptr;
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -34,6 +34,8 @@ auto pTableView::construct() -> void {
|
|||
setBordered(state().bordered);
|
||||
setFont(self().font(true));
|
||||
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-release-event", G_CALLBACK(TableView_buttonEvent), (gpointer)this);
|
||||
|
@ -50,7 +52,7 @@ auto pTableView::destruct() -> void {
|
|||
gtk_widget_destroy(gtkWidget);
|
||||
}
|
||||
|
||||
auto pTableView::append(sTableViewHeader header) -> void {
|
||||
auto pTableView::append(sTableViewColumn column) -> void {
|
||||
}
|
||||
|
||||
auto pTableView::append(sTableViewItem item) -> void {
|
||||
|
@ -60,24 +62,23 @@ auto pTableView::focused() const -> bool {
|
|||
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::resizeColumns() -> void {
|
||||
lock();
|
||||
auto lock = acquire();
|
||||
|
||||
if(auto& header = state().header) {
|
||||
vector<signed> widths;
|
||||
signed minimumWidth = 0;
|
||||
signed expandable = 0;
|
||||
for(auto column : range(header->columnCount())) {
|
||||
signed width = _width(column);
|
||||
for(uint position : range(self().columnCount())) {
|
||||
signed width = _width(position);
|
||||
widths.append(width);
|
||||
minimumWidth += width;
|
||||
if(header->column(column).expandable()) expandable++;
|
||||
if(self().column(position).expandable()) expandable++;
|
||||
}
|
||||
|
||||
signed maximumWidth = self().geometry().width() - 6;
|
||||
|
@ -92,16 +93,13 @@ auto pTableView::resizeColumns() -> void {
|
|||
expandWidth = (maximumWidth - minimumWidth) / expandable;
|
||||
}
|
||||
|
||||
for(auto column : range(header->columnCount())) {
|
||||
if(auto self = header->state.columns[column]->self()) {
|
||||
signed width = widths[column];
|
||||
if(self->state().expandable) width += expandWidth;
|
||||
gtk_tree_view_column_set_fixed_width(self->gtkColumn, width);
|
||||
for(uint position : range(self().columnCount())) {
|
||||
if(auto column = self().column(position)->self()) {
|
||||
signed width = widths[position];
|
||||
if(column->state().expandable) width += expandWidth;
|
||||
gtk_tree_view_column_set_fixed_width(column->gtkColumn, width);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unlock();
|
||||
}
|
||||
|
||||
auto pTableView::setAlignment(Alignment alignment) -> void {
|
||||
|
@ -131,9 +129,6 @@ auto pTableView::setFocused() -> 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 {
|
||||
|
@ -143,10 +138,18 @@ auto pTableView::setForegroundColor(Color color) -> void {
|
|||
|
||||
auto pTableView::setGeometry(Geometry geometry) -> void {
|
||||
pWidget::setGeometry(geometry);
|
||||
if(auto& header = state().header) {
|
||||
for(auto& column : header->state.columns) {
|
||||
if(column->state.expandable) return resizeColumns();
|
||||
for(auto& column : state().columns) {
|
||||
if(column->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 {
|
||||
unsigned width = 8;
|
||||
if(auto& header = state().header) {
|
||||
if(auto column = header->column(_column)) {
|
||||
if(auto column = self().column(_column)) {
|
||||
if(auto& icon = column->state.icon) {
|
||||
width += icon.width() + 2;
|
||||
}
|
||||
if(auto& text = column->state.text) {
|
||||
width += pFont::size(column->font(true), text).width();
|
||||
}
|
||||
if(column->state.sorting != Sort::None) {
|
||||
width += 20;
|
||||
}
|
||||
}
|
||||
return width;
|
||||
|
@ -191,8 +195,7 @@ auto pTableView::_createModel() -> void {
|
|||
gtkTreeModel = nullptr;
|
||||
|
||||
vector<GType> types;
|
||||
if(auto& header = state().header) {
|
||||
for(auto column : header->state.columns) {
|
||||
for(auto& column : state().columns) {
|
||||
if(auto self = column->self()) {
|
||||
if(!self->gtkColumn) continue; //may not have been created yet; or recently destroyed
|
||||
types.append(G_TYPE_BOOLEAN);
|
||||
|
@ -200,7 +203,6 @@ auto pTableView::_createModel() -> void {
|
|||
types.append(G_TYPE_STRING);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!types) return; //no columns available
|
||||
|
||||
gtkListStore = gtk_list_store_newv(types.size(), types.data());
|
||||
|
@ -230,8 +232,7 @@ auto pTableView::_doDataFunc(GtkTreeViewColumn* gtkColumn, GtkCellRenderer* rend
|
|||
auto row = toNatural(path);
|
||||
g_free(path);
|
||||
|
||||
if(auto& header = state().header) {
|
||||
for(auto& column : header->state.columns) {
|
||||
for(auto& column : state().columns) {
|
||||
if(auto p = column->self()) {
|
||||
if(renderer != GTK_CELL_RENDERER(p->gtkCellToggle)
|
||||
&& 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 {
|
||||
if(auto& header = state().header) {
|
||||
for(auto& column : header->state.columns) {
|
||||
for(auto& column : state().columns) {
|
||||
if(auto delegate = column->self()) {
|
||||
if(gtkCellRendererText == GTK_CELL_RENDERER_TEXT(delegate->gtkCellText)) {
|
||||
auto row = toNatural(path);
|
||||
|
@ -293,7 +292,6 @@ auto pTableView::_doEdit(GtkCellRendererText* gtkCellRendererText, const char* p
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto pTableView::_doEvent(GdkEventButton* event) -> signed {
|
||||
|
@ -326,8 +324,7 @@ auto pTableView::_doEvent(GdkEventButton* event) -> signed {
|
|||
}
|
||||
|
||||
auto pTableView::_doHeaderActivate(GtkTreeViewColumn* gtkTreeViewColumn) -> void {
|
||||
if(auto& header = state().header) {
|
||||
for(auto& column : header->state.columns) {
|
||||
for(auto& column : state().columns) {
|
||||
if(auto delegate = column->self()) {
|
||||
if(gtkTreeViewColumn == delegate->gtkColumn) {
|
||||
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
|
||||
|
@ -348,8 +344,7 @@ auto pTableView::_doMouseMove() -> signed {
|
|||
}
|
||||
|
||||
auto pTableView::_doToggle(GtkCellRendererToggle* gtkCellRendererToggle, const char* path) -> void {
|
||||
if(auto& header = state().header) {
|
||||
for(auto& column : header->state.columns) {
|
||||
for(auto& column : state().columns) {
|
||||
if(auto delegate = column->self()) {
|
||||
if(gtkCellRendererToggle == GTK_CELL_RENDERER_TOGGLE(delegate->gtkCellToggle)) {
|
||||
auto row = toNatural(path);
|
||||
|
@ -363,7 +358,6 @@ auto pTableView::_doToggle(GtkCellRendererToggle* gtkCellRendererToggle, const c
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//compare currently selected items to previously selected items
|
||||
|
@ -412,17 +406,14 @@ auto pTableView::_updateSelected() -> void {
|
|||
}
|
||||
|
||||
auto pTableView::_width(unsigned column) -> unsigned {
|
||||
if(auto& header = state().header) {
|
||||
if(auto width = header->column(column).width()) return width;
|
||||
if(auto width = self().column(column).width()) return width;
|
||||
unsigned width = 1;
|
||||
if(!header->column(column).visible()) return width;
|
||||
if(header->visible()) width = max(width, _columnWidth(column));
|
||||
if(!self().column(column).visible()) return width;
|
||||
if(self().headered()) width = max(width, _columnWidth(column));
|
||||
for(auto row : range(self().itemCount())) {
|
||||
width = max(width, _cellWidth(row, column));
|
||||
}
|
||||
return width;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,10 +5,10 @@ namespace hiro {
|
|||
struct pTableView : pWidget {
|
||||
Declare(TableView, Widget)
|
||||
|
||||
auto append(sTableViewHeader column) -> void;
|
||||
auto append(sTableViewColumn column) -> void;
|
||||
auto append(sTableViewItem item) -> void;
|
||||
auto focused() const -> bool override;
|
||||
auto remove(sTableViewHeader column) -> void;
|
||||
auto remove(sTableViewColumn column) -> void;
|
||||
auto remove(sTableViewItem item) -> void;
|
||||
auto resizeColumns() -> void;
|
||||
auto setAlignment(Alignment alignment) -> void;
|
||||
|
@ -19,21 +19,23 @@ struct pTableView : pWidget {
|
|||
auto setFont(const Font& font) -> void override;
|
||||
auto setForegroundColor(Color color) -> void;
|
||||
auto setGeometry(Geometry geometry) -> void override;
|
||||
auto setHeadered(bool headered) -> void;
|
||||
auto setSortable(bool sortable) -> void;
|
||||
|
||||
auto _cellWidth(unsigned row, unsigned column) -> unsigned;
|
||||
auto _columnWidth(unsigned column) -> unsigned;
|
||||
auto _cellWidth(uint row, uint column) -> uint;
|
||||
auto _columnWidth(uint column) -> uint;
|
||||
auto _createModel() -> void;
|
||||
auto _doActivate() -> void;
|
||||
auto _doChange() -> void;
|
||||
auto _doContext() -> void;
|
||||
auto _doDataFunc(GtkTreeViewColumn* column, GtkCellRenderer* renderer, GtkTreeIter* iter) -> 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 _doMouseMove() -> signed;
|
||||
auto _doMouseMove() -> int;
|
||||
auto _doToggle(GtkCellRendererToggle* gtkCellRendererToggle, const char* path) -> void;
|
||||
auto _updateSelected() -> void;
|
||||
auto _width(unsigned column) -> unsigned;
|
||||
auto _width(uint column) -> uint;
|
||||
|
||||
GtkScrolledWindow* gtkScrolledWindow = nullptr;
|
||||
GtkWidget* gtkWidgetChild = nullptr;
|
||||
|
@ -41,7 +43,7 @@ struct pTableView : pWidget {
|
|||
GtkTreeSelection* gtkTreeSelection = nullptr;
|
||||
GtkListStore* gtkListStore = nullptr;
|
||||
GtkTreeModel* gtkTreeModel = nullptr;
|
||||
vector<unsigned> currentSelection;
|
||||
vector<uint> currentSelection;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}}
|
|
@ -3,7 +3,7 @@
|
|||
namespace nall { namespace Encode {
|
||||
|
||||
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};
|
||||
if(!fp) return false;
|
||||
|
||||
|
@ -35,7 +35,7 @@ struct BMP {
|
|||
|
||||
pitch >>= 2;
|
||||
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);
|
||||
if(paddingLength) fp.writel(0, paddingLength);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}}
|
|
@ -7,6 +7,8 @@ namespace nall {
|
|||
template<typename T> struct function;
|
||||
|
||||
template<typename R, typename... P> struct function<auto (P...) -> R> {
|
||||
using cast = auto (*)(P...) -> R;
|
||||
|
||||
//value = true if auto L::operator()(P...) -> R exists
|
||||
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;
|
||||
|
@ -16,11 +18,11 @@ template<typename R, typename... P> struct function<auto (P...) -> R> {
|
|||
|
||||
function() {}
|
||||
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); }
|
||||
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 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; }
|
||||
|
||||
explicit operator bool() const { return callback; }
|
||||
|
@ -35,6 +37,12 @@ template<typename R, typename... P> struct function<auto (P...) -> R> {
|
|||
return *this;
|
||||
}
|
||||
|
||||
auto operator=(void* source) -> function& {
|
||||
if(callback) { delete callback; callback = nullptr; }
|
||||
callback = new global((auto (*)(P...) -> R)source);
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
struct container {
|
||||
virtual auto operator()(P... p) const -> R = 0;
|
||||
|
|
151
nall/image.hpp
151
nall/image.hpp
|
@ -7,7 +7,156 @@
|
|||
#include <nall/stdint.hpp>
|
||||
#include <nall/decode/bmp.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/core.hpp>
|
||||
#include <nall/image/load.hpp>
|
||||
|
|
|
@ -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};
|
||||
};
|
||||
|
||||
}
|
|
@ -160,4 +160,14 @@ auto image::allocate(unsigned width, unsigned height, unsigned stride) -> uint8_
|
|||
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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -47,6 +47,9 @@ template<> struct view<string> {
|
|||
inline auto data() const -> const char*;
|
||||
inline auto size() const -> uint;
|
||||
|
||||
inline auto begin() const { return &_data[0]; }
|
||||
inline auto end() const { return &_data[size()]; }
|
||||
|
||||
protected:
|
||||
string* _string;
|
||||
const char* _data;
|
||||
|
@ -188,6 +191,8 @@ public:
|
|||
inline auto length() const -> uint;
|
||||
|
||||
//find.hpp
|
||||
inline auto contains(view<string> characters) const -> maybe<uint>;
|
||||
|
||||
template<bool, bool> inline auto _find(int, view<string>) 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/pascal.hpp>
|
||||
|
||||
#include <nall/string/atoi.hpp>
|
||||
#include <nall/string/cast.hpp>
|
||||
#include <nall/string/compare.hpp>
|
||||
|
@ -320,13 +327,16 @@ struct string_format : vector<string> {
|
|||
#include <nall/string/trim.hpp>
|
||||
#include <nall/string/utility.hpp>
|
||||
#include <nall/string/vector.hpp>
|
||||
|
||||
#include <nall/string/eval/node.hpp>
|
||||
#include <nall/string/eval/literal.hpp>
|
||||
#include <nall/string/eval/parser.hpp>
|
||||
#include <nall/string/eval/evaluator.hpp>
|
||||
|
||||
#include <nall/string/markup/node.hpp>
|
||||
#include <nall/string/markup/find.hpp>
|
||||
#include <nall/string/markup/bml.hpp>
|
||||
#include <nall/string/markup/xml.hpp>
|
||||
|
||||
#include <nall/string/transform/cml.hpp>
|
||||
#include <nall/string/transform/dml.hpp>
|
||||
|
|
|
@ -234,6 +234,20 @@ template<> struct stringify<const view<string>&> {
|
|||
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
|
||||
|
||||
template<typename T> struct stringify<T*> {
|
||||
|
|
|
@ -2,6 +2,15 @@
|
|||
|
||||
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> {
|
||||
if(source.size() == 0) return nothing;
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ protected:
|
|||
p += length;
|
||||
}
|
||||
|
||||
auto parseData(const char*& p) -> void {
|
||||
auto parseData(const char*& p, view<string> spacing) -> void {
|
||||
if(*p == '=' && *(p + 1) == '\"') {
|
||||
uint length = 2;
|
||||
while(p[length] && p[length] != '\n' && p[length] != '\"') length++;
|
||||
|
@ -56,13 +56,13 @@ protected:
|
|||
} else if(*p == ':') {
|
||||
uint length = 1;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
//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') {
|
||||
if(*p != ' ') throw "Invalid node name";
|
||||
while(*p == ' ') p++; //skip excess spaces
|
||||
|
@ -73,31 +73,31 @@ protected:
|
|||
while(valid(p[length])) length++;
|
||||
if(length == 0) throw "Invalid attribute name";
|
||||
node->_name = slice(p, 0, length);
|
||||
node->parseData(p += length);
|
||||
node->parseData(p += length, spacing);
|
||||
node->_value.trimRight("\n", 1L);
|
||||
_children.append(node);
|
||||
}
|
||||
}
|
||||
|
||||
//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++];
|
||||
_metadata = parseDepth(p);
|
||||
parseName(p);
|
||||
parseData(p);
|
||||
parseAttributes(p);
|
||||
parseData(p, spacing);
|
||||
parseAttributes(p, spacing);
|
||||
|
||||
while(y < text.size()) {
|
||||
uint depth = readDepth(text[y]);
|
||||
if(depth <= _metadata) break;
|
||||
|
||||
if(text[y][depth] == ':') {
|
||||
_value.append(slice(text[y++], depth + 1), "\n");
|
||||
_value.append(slice(text[y++], depth + 1).trimLeft(spacing, 1L), "\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
SharedNode node(new ManagedNode);
|
||||
node->parseNode(text, y);
|
||||
node->parseNode(text, y, spacing);
|
||||
_children.append(node);
|
||||
}
|
||||
|
||||
|
@ -105,7 +105,7 @@ protected:
|
|||
}
|
||||
|
||||
//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
|
||||
//the below code will turn '\r\n' into '\n'; skip empty lines; and skip comment lines
|
||||
char* p = document.get(), *output = p;
|
||||
|
@ -134,37 +134,37 @@ protected:
|
|||
uint y = 0;
|
||||
while(y < text.size()) {
|
||||
SharedNode node(new ManagedNode);
|
||||
node->parseNode(text, y);
|
||||
node->parseNode(text, y, spacing);
|
||||
if(node->_metadata > 0) throw "Root nodes cannot be indented";
|
||||
_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);
|
||||
try {
|
||||
node->parse(markup);
|
||||
node->parse(markup, spacing);
|
||||
} catch(const char* error) {
|
||||
node.reset();
|
||||
}
|
||||
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()) {
|
||||
string result;
|
||||
for(auto leaf : node) {
|
||||
result.append(serialize(leaf, depth));
|
||||
result.append(serialize(leaf, spacing, depth));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
string padding;
|
||||
padding.resize(depth * 2);
|
||||
for(auto& byte : padding) byte = ' ';
|
||||
padding.fill(' ');
|
||||
|
||||
vector<string> lines;
|
||||
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;
|
||||
result.append(padding);
|
||||
result.append(node.name());
|
||||
if(lines.size() == 1) result.append(":", lines[0]);
|
||||
if(lines.size() == 1) result.append(":", spacing, lines[0]);
|
||||
result.append("\n");
|
||||
if(lines.size() > 1) {
|
||||
padding.append(" ");
|
||||
for(auto& line : lines) {
|
||||
result.append(padding, ":", line, "\n");
|
||||
result.append(padding, ":", spacing, line, "\n");
|
||||
}
|
||||
}
|
||||
for(auto leaf : node) {
|
||||
result.append(serialize(leaf, depth + 1));
|
||||
result.append(serialize(leaf, spacing, depth + 1));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
|
@ -110,7 +110,10 @@ struct vector_base {
|
|||
|
||||
//utility.hpp
|
||||
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 foreach(const function<void (const T&)>& callback) -> void;
|
||||
auto foreach(const function<void (uint, const T&)>& callback) -> void;
|
||||
|
||||
private:
|
||||
T* _pool = nullptr; //pointer to first initialized element in pool
|
||||
|
|
|
@ -6,9 +6,22 @@ template<typename T> auto vector<T>::sort(const function<bool (const T& lhs, con
|
|||
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> {
|
||||
for(uint n : range(size())) if(_pool[n] == value) return n;
|
||||
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]);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue