mirror of
https://github.com/aseprite/aseprite.git
synced 2025-04-06 16:20:30 +00:00
289 lines
8.1 KiB
C++
289 lines
8.1 KiB
C++
// Aseprite Code Generator
|
|
// Copyright (c) 2021-2024 Igara Studio S.A.
|
|
// Copyright (c) 2014-2018 David Capello
|
|
//
|
|
// This file is released under the terms of the MIT license.
|
|
// Read LICENSE.txt for more information.
|
|
|
|
#include "gen/ui_class.h"
|
|
|
|
#include "base/exception.h"
|
|
#include "base/file_handle.h"
|
|
#include "base/fs.h"
|
|
#include "base/string.h"
|
|
#include "gen/common.h"
|
|
|
|
#include <iostream>
|
|
#include <set>
|
|
#include <vector>
|
|
|
|
using namespace tinyxml2;
|
|
using XmlElements = std::vector<XMLElement*>;
|
|
|
|
namespace {
|
|
|
|
struct Item {
|
|
std::string xmlid;
|
|
std::string cppid;
|
|
std::string type;
|
|
std::string incl;
|
|
const Item& typeIncl(const char* type,
|
|
const char* incl) {
|
|
this->type = type;
|
|
this->incl = incl;
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
static XMLElement* find_element_by_id(XMLElement* elem, const std::string& thisId)
|
|
{
|
|
const char* id = elem->Attribute("id");
|
|
if (id && id == thisId)
|
|
return elem;
|
|
|
|
XMLElement* child = elem->FirstChildElement();
|
|
while (child) {
|
|
XMLElement* match = find_element_by_id(child, thisId);
|
|
if (match)
|
|
return match;
|
|
|
|
child = child->NextSiblingElement();
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void collect_widgets_with_ids(XMLElement* elem, XmlElements& widgets)
|
|
{
|
|
XMLElement* child = elem->FirstChildElement();
|
|
while (child) {
|
|
const char* id = child->Attribute("id");
|
|
if (id)
|
|
widgets.push_back(child);
|
|
collect_widgets_with_ids(child, widgets);
|
|
child = child->NextSiblingElement();
|
|
}
|
|
}
|
|
|
|
static Item convert_to_item(XMLElement* elem)
|
|
{
|
|
static std::string parent;
|
|
const std::string name = elem->Value();
|
|
if (name != "item")
|
|
parent = name;
|
|
|
|
Item item;
|
|
if (elem->Attribute("id")) {
|
|
item.xmlid = elem->Attribute("id");
|
|
item.cppid = convert_xmlid_to_cppid(item.xmlid, false);
|
|
}
|
|
|
|
if (name == "box")
|
|
return item.typeIncl("ui::Box",
|
|
"ui/box.h");
|
|
if (name == "boxfiller")
|
|
return item.typeIncl("ui::Box",
|
|
"ui/box.h");
|
|
if (name == "button")
|
|
return item.typeIncl("ui::Button",
|
|
"ui/button.h");
|
|
if (name == "buttonset")
|
|
return item.typeIncl("app::ButtonSet",
|
|
"app/ui/button_set.h");
|
|
if (name == "check") {
|
|
if (elem->Attribute("pref") != nullptr)
|
|
return item.typeIncl("BoolPrefWidget<ui::CheckBox>",
|
|
"app/ui/pref_widget.h");
|
|
else
|
|
return item.typeIncl("ui::CheckBox",
|
|
"ui/button.h");
|
|
}
|
|
if (name == "colorpicker")
|
|
return item.typeIncl("app::ColorButton",
|
|
"app/ui/color_button.h");
|
|
if (name == "combobox")
|
|
return item.typeIncl("ui::ComboBox",
|
|
"ui/combobox.h");
|
|
if (name == "dropdownbutton")
|
|
return item.typeIncl("app::DropDownButton",
|
|
"app/ui/drop_down_button.h");
|
|
if (name == "entry")
|
|
return item.typeIncl("ui::Entry",
|
|
"ui/entry.h");
|
|
if (name == "expr")
|
|
return item.typeIncl("app::ExprEntry",
|
|
"app/ui/expr_entry.h");
|
|
if (name == "grid")
|
|
return item.typeIncl("ui::Grid",
|
|
"ui/grid.h");
|
|
if (name == "hbox")
|
|
return item.typeIncl("ui::HBox",
|
|
"ui/box.h");
|
|
if (name == "image")
|
|
return item.typeIncl("ui::ImageView",
|
|
"ui/image_view.h");
|
|
if (name == "item" &&
|
|
parent == "buttonset")
|
|
return item.typeIncl("app::ButtonSet::Item",
|
|
"app/ui/button_set.h");
|
|
if (name == "label")
|
|
return item.typeIncl("ui::Label",
|
|
"ui/label.h");
|
|
if (name == "link")
|
|
return item.typeIncl("ui::LinkLabel",
|
|
"ui/link_label.h");
|
|
if (name == "listbox")
|
|
return item.typeIncl("ui::ListBox",
|
|
"ui/listbox.h");
|
|
if (name == "listitem")
|
|
return item.typeIncl("ui::ListItem",
|
|
"ui/listitem.h");
|
|
if (name == "panel")
|
|
return item.typeIncl("ui::Panel",
|
|
"ui/panel.h");
|
|
if (name == "popupwindow")
|
|
return item.typeIncl("ui::PopupWindow",
|
|
"ui/popup_window.h");
|
|
if (name == "radio")
|
|
return item.typeIncl("ui::RadioButton",
|
|
"ui/button.h");
|
|
if (name == "search")
|
|
return item.typeIncl("app::SearchEntry",
|
|
"app/ui/search_entry.h");
|
|
if (name == "separator")
|
|
return item.typeIncl("ui::Separator",
|
|
"ui/separator.h");
|
|
if (name == "slider")
|
|
return item.typeIncl("ui::Slider",
|
|
"ui/slider.h");
|
|
if (name == "alphaslider" || name == "opacityslider")
|
|
return item.typeIncl("app::AlphaSlider",
|
|
"app/ui/alpha_slider.h");
|
|
if (name == "splitter")
|
|
return item.typeIncl("ui::Splitter",
|
|
"ui/splitter.h");
|
|
if (name == "textbox")
|
|
return item.typeIncl("ui::TextBox",
|
|
"ui/textbox.h");
|
|
if (name == "tipwindow")
|
|
return item.typeIncl("ui::TipWindow",
|
|
"ui/tooltips.h");
|
|
if (name == "vbox")
|
|
return item.typeIncl("ui::VBox",
|
|
"ui/box.h");
|
|
if (name == "view")
|
|
return item.typeIncl("ui::View",
|
|
"ui/view.h");
|
|
if (name == "window")
|
|
return item.typeIncl("ui::Window",
|
|
"ui/window.h");
|
|
|
|
throw base::Exception("Unknown widget name: " + name);
|
|
}
|
|
|
|
void gen_ui_class(XMLDocument* doc,
|
|
const std::string& inputFn,
|
|
const std::string& widgetId)
|
|
{
|
|
std::cout
|
|
<< "// Don't modify, generated file from " << inputFn << "\n"
|
|
<< "\n";
|
|
|
|
XMLHandle handle(doc);
|
|
XMLElement* elem = handle.FirstChildElement("gui").ToElement();
|
|
elem = find_element_by_id(elem, widgetId);
|
|
if (!elem) {
|
|
std::cout << "#error Widget not found: " << widgetId << "\n";
|
|
return;
|
|
}
|
|
|
|
std::vector<Item> items;
|
|
{
|
|
XmlElements xmlWidgets;
|
|
collect_widgets_with_ids(elem, xmlWidgets);
|
|
for (XMLElement* elem : xmlWidgets) {
|
|
const char* id = elem->Attribute("id");
|
|
if (!id)
|
|
continue;
|
|
items.push_back(convert_to_item(elem));
|
|
}
|
|
}
|
|
|
|
std::string className = convert_xmlid_to_cppid(widgetId, true);
|
|
std::string fnUpper = base::string_to_upper(base::get_file_title(inputFn));
|
|
Item mainWidget = convert_to_item(elem);
|
|
|
|
std::set<std::string> headerFiles;
|
|
headerFiles.insert("app/find_widget.h");
|
|
headerFiles.insert("app/load_widget.h");
|
|
headerFiles.insert(mainWidget.incl);
|
|
for (const Item& item : items)
|
|
headerFiles.insert(item.incl);
|
|
|
|
std::cout
|
|
<< "#ifndef GENERATED_" << fnUpper << "_H_INCLUDED\n"
|
|
<< "#define GENERATED_" << fnUpper << "_H_INCLUDED\n"
|
|
<< "#pragma once\n"
|
|
<< "\n";
|
|
for (const auto& incl : headerFiles)
|
|
std::cout << "#include \"" << incl << "\"\n";
|
|
std::cout
|
|
<< "\n"
|
|
<< "namespace app {\n"
|
|
<< "namespace gen {\n"
|
|
<< "\n"
|
|
<< " class " << className << " : public " << mainWidget.type << " {\n"
|
|
<< " public:\n"
|
|
<< " " << className << "()";
|
|
|
|
// Special ctor for base class
|
|
if (mainWidget.type == "ui::Window") {
|
|
const char* desktop = elem->Attribute("desktop");
|
|
if (desktop && std::string(desktop) == "true")
|
|
std::cout
|
|
<< " : ui::Window(ui::Window::DesktopWindow)";
|
|
else
|
|
std::cout
|
|
<< " : ui::Window(ui::Window::WithTitleBar)";
|
|
}
|
|
|
|
std::cout
|
|
<< " {\n"
|
|
<< " app::load_widget(\"" << base::get_file_name(inputFn) << "\", \"" << widgetId << "\", this);\n"
|
|
<< " app::finder(this)\n";
|
|
|
|
for (const Item& item : items) {
|
|
std::cout
|
|
<< " >> \"" << item.xmlid << "\" >> m_" << item.cppid << "\n";
|
|
}
|
|
|
|
std::cout
|
|
<< " ;\n"
|
|
<< " }\n"
|
|
<< "\n";
|
|
|
|
for (const Item& item : items) {
|
|
std::cout
|
|
<< " " << item.type << "* " << item.cppid << "() const { return m_" << item.cppid << "; }\n";
|
|
}
|
|
|
|
std::cout
|
|
<< "\n"
|
|
<< " private:\n";
|
|
|
|
for (const Item& item : items) {
|
|
std::cout
|
|
<< " " << item.type << "* m_" << item.cppid << ";\n";
|
|
}
|
|
|
|
std::cout
|
|
<< " };\n"
|
|
<< "\n"
|
|
<< "} // namespace gen\n"
|
|
<< "} // namespace app\n"
|
|
<< "\n"
|
|
<< "#endif\n";
|
|
}
|