-
Notifications
You must be signed in to change notification settings - Fork 58
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add QML Bindings for (almost all) liblcf data #205
base: master
Are you sure you want to change the base?
Conversation
Contains convenience functions get for 1-index and get0 for 0-index. It will be necessary to have more features like "Add Object to Array" later.
Hi, I have made a library for visual Assets called "ez" with my limited knowledge on QML. property string assets: pathIsLocal ? Qt.resolvedUrl(".") : "https://raw.githubusercontent.com/jetrotal/easyRPG_UI/main/";
background:ez.bg;
component UI_Library : Item {
visible:false
property real resizeFactor: 1
property var style: {
"sizes": {
"text":9 * resizeFactor,
"topBar":27 * resizeFactor,
"logo": 16 * resizeFactor,
"win":16 * resizeFactor,
"icon":24 * resizeFactor,
"checkboxSpacing":15 * resizeFactor,
"checkboxOuter":11 * resizeFactor,
"checkboxInner":5 * resizeFactor,
},
"colors": {
"bg": "#292b2f",
"header": "#242529",
"textA": "#97999C",
"textB": "#FFF",
"highlight": "#8AFF4E",
"separator": "#555",
"darkBorders": "#1e1e1e",
"shadowColor":"#222327"
},
"images": {
"logo": assets + "img/ez-logo.svg",
"bg": assets + "img/bg/tile.svg",
"icons": {
"win": {
"url": assets + "img/icons/window/"
},
"top": {
"url": assets + "img/icons/top/"
}
}
}
} // STYLES
property var appButtons: {
"workSpace": [{
"obj": "appHome",
"display": "Home",
},/*separator*/, {
"obj": "mapEditor",
"display": "Map Editor",
}, {
"obj": "databaseEditor",
"display": "Database Editor",
}, {
"obj": "eventsEditor",
"display": "Events Editor",
},/*separator*/, {
"obj": "resourceManager",
"display": "Resource Manager",
}, {
"obj": "gameSearch",
"display": "Search",
},/*separator*/, {
"obj": "bgmTest",
"display": "BGM test",
},/*separator*/, {
"obj": "gameSettings",
"display": "Playtest Settings",
}, {
"obj": "gamePlay",
"display": "Begin Playtest",
}],
"window": [{
"obj": "min",
"display": "Minimize Window",
}, {
"obj": "max",
"display": "Maximize Window",
}, {
"obj": "close",
"display": "Close Window",
}],
"topBar": [{
"obj": "project",
"display": "Game Project",
"contents": [{
"obj":"newProject","display":"New Project", "shortcut":"Ctrl+N", "disabled":1
}, {
"obj":"openProject","display":"Open Project", "shortcut":"Ctrl+O", "disabled":1
}, {
"obj":"closeProject","display":"Close Project", "shortcut":"Ctrl+X"
},/*separator*/, {
"obj":"createDisk","display":"Create Game Disk"
},/*separator*/, {
"obj":"quit","display":"Quit"
}]
}, {
"obj": "maps",
"display": "Maps",
"contents": [{
"obj":"saveMap","display":"Save Map", "shortcut":"Ctrl+S"
}, {
"obj":"revertMap","display":"Revert Map", "shortcut":"Ctrl+R"
},/*separator*/, {
"obj":"lowerLayer","display":"Lower Layer", "shortcut":"F5"
}, {
"obj":"upperLayer","display":"Upper Layer", "shortcut":"F6"
}, {
"obj":"eventsLayer","display":"Events Layer", "shortcut":"F7"
},/*separator*/, {
"obj":"defaultZoom","display":"Zoom 100%"
}, {
"obj":"zoomIn","display":"Zoom In", "shortcut":"+"
}, {
"obj":"zoomOut","display":"Zoom Out", "shortcut":"-"
}]
}, {
"obj": "view",
"display": "View",
"contents": [{
"obj":"palette","display":"Palette","checkbox":1,"checked":1
}, {
"obj":"mapTree","display":"Map Tree","checkbox":1,"checked":1
}]
}, {
"obj": "tools",
"display": "Tools",
"contents": [{
"obj":"database","display":"DataBase", "shortcut":"F8"
}, {
"obj":"resourceManager","display":"Resource Manager"
}, {
"obj":"jukebox","display":"Jukebox"
}, {
"obj":"search","display":"Search"
},]
}, {
"obj": "game",
"display": "Game",
"contents": [{
"obj":"playTest","display":"PlayTest", "shortcut":"F4"
},/*separator*/, {
"obj":"fullScreen","display":"Fullscreen", "checkbox":1,"checked":1
}, {
"obj":"showTitle","display":"Show Title Background","checkbox":1,"checked":1
},/*separator*/, {
"obj":"playSettings","display":"PlayTest Settings"
},]
}, {
"obj": "help",
"display": "Help",
"contents": [{
"obj":"contents","display":"Contents", "shortcut":"F1"
},/*separator*/, {
"obj":"about","display":"About"
}, {
"obj":"aboutQt","display":"About Qt"
},]
}, {
"obj": "debug",
"display": "Debug",
"contents": [{
"obj":"rtpPath","display":"RTP Path"
}, {
"obj":"caching","display":"Enable Caching","checkbox":1,"checked":1
},/*separator*/, {
"obj":"displayButtons","display":"Display Button Actions","checkbox":1,"checked":debugParams.displayButtons * debug
}, {
"obj":"displayBorders","display":"Display Invisible Borders","checkbox":1,"checked":debugParams.displayBorders * debug
},, {
"obj":"listProperties","display":"List Properties on Console","checkbox":1,"checked":debugParams.listProperties * debug
},]
}]
}
function listProperties(item) {
// debug props
var result="";
for (var p in item) result += String(item[p]) !== "function() { [native code] }" ? (p +": " + (String(item[p]) ==="[object Object]" ?" {; "+ listProperties(item[p])+" } " : item[p]) +`;`):"";
return(result.split(';').join(pathIsLocal ? "\n" : "<br>"))
}
function detectCommand(bt, type,qmlItem) {
if (type[1] === "debug") debugFunction(bt)
if (debugParams.displayButtons===1) footerMark.text = ("running detectCommand() - You clicked: " + JSON.stringify(bt.obj) + " | Type: "+JSON.stringify(type))
else footerMark.text="";
if (debugParams.listProperties===1) console.log(ez.listProperties(qmlItem))
if (type === "workspace") return changeWorkspace(bt)
if (type === "window") return resizeWindow(bt)
}
function debugFunction(bt){
console.log(bt.obj +": "+debugParams[bt.obj])
debugParams[bt.obj] = debugParams[bt.obj] === 0 ? 1 : 0
contentBody.border.width = debugParams.displayBorders
}
function resizeWindow(bt) {
if (bt.obj === "min") return app.showMinimized()
if (bt.obj === "max") return console.log(app.visibility), app.visibility === 4 ? app.showNormal() :app.showMaximized()
if (bt.obj === "close") return app.close()
}
function changeWorkspace(e) {
currWorkspace.text = "<pre><font color='" + styles.colors.textB + "'> " + e.display + " </pre>"
}
property Item bg: Rectangle {
anchors.fill: parent
color: ez.style.colors.bg
Image {
anchors.fill:parent
fillMode: Image.Tile
source: ez.style.images.bg
}
}
}
UI_Library {
id:ez
} // ez[variableName] // ez.styles[styleVar] // The full file is here: https://github.com/jetrotal/easyRPG_UI/blob/main/scripts/qml/index.qml I failed to make it work as a typical QML file since I was not able to install the Editor's projec, or kirigami or any other library on my computer. But, I hope that helps on something; |
This expoeses liblcf data structures to QML. QWidgets are kinda obsolute, so this is a first step in direction of QML.
Without this binding using QML is just extremely painful because the entire API relies on the Meta Object Compiler.
It uses a code generator. I investigated manual alternatives for many days and a generator just works best here.
The binding is holding pointers to all the lcf data, so it is a thin, non-owning wrapper. Of course all this wrapping is not for free but that cannot be really avoided when crossing C++ <-> Javascript/QML boundaries. But this is an editor so performance is not a mayor concern compared to the Player.
With this change it is possible to write code like this in QML:
Also data binding becomes super easy this way. E.g. to edit the current actor name in the QML Ui you can write (just an example, not included yet):
We could even provide a way to generate e.g. these Actor curves from a expression.
Want quadratic hp growth? Easy:
Content:
src/binding/generated
contains the generated API. This is generated frombinding_generator/generator.py
. Yes the generator is based on the liblcf generator and also consumes the CSV files from it.src/binding
the files in here inherit from the generated API. This way it is possible to add more exported properties and functions for QML if necessary. Most of them will likely stay empty for a long time or forever but these files give us more flexibility.To demonstrate that the extension API works I added as an example to Actor:
Now all actors have the new method
sayHello
available in QML.TODO I will resolve when it starts to become a problem:
ArrayAdapter
.