otclient-redemption/modules/dev_otui/dev_otui.otui
Renan Miyashiro 9bfac7719f
feat(dev_otui): visual .otui screen editor inside the client (#1782)
New Features:
- Added a visual OTUI editor for previewing and editing .otui files, including live preview, widget selection, and editing of properties, anchors, geometry and states.
- Supports adding/removing widgets and properties, creating new screens, and saving back with safety checks and backups.
- Included file/style/anchor pickers and a style gallery with live samples.
- Added an OTML parser/writer to preserve and apply line-accurate edits.

Documentation:
- Added comprehensive editor documentation, workflows and operational notes.
2026-07-31 19:01:14 -03:00

409 lines
9.1 KiB
Text

OtuiTreeItem < UILabel
height: 14
font: verdana-11px-monochrome
color: #c0c0c0
background-color: alpha
text-offset: 2 0
focusable: true
phantom: false
$focus:
background-color: #585858
color: #ffffff
OtuiSectionLabel < UILabel
height: 12
font: verdana-11px-monochrome
color: #8f8f8f
margin-top: 5
OtuiStateCheck < CheckBox
width: 90
height: 14
font: verdana-11px-monochrome
color: #c0c0c0
# One row of the property panel: name, editable value and a remove button.
#
# focusable MUST be true here and on propList: recursiveFocus() only focuses
# the focusable levels of the hierarchy (uiwidget.cpp:876) and
# propagateOnKeyText() only descends into focused children (uiwidget.cpp:2082).
# With a non-focusable link in the chain, the row's TextEdit never receives
# what you type.
OtuiPropRow < UIWidget
height: 18
focusable: true
UILabel
id: keyLabel
width: 118
height: 16
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
font: verdana-11px-monochrome
color: #9fc6e0
text-offset: 2 1
Button
id: removeBtn
!text: 'x'
width: 16
height: 16
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
TextEdit
id: valueEdit
height: 18
anchors.left: keyLabel.right
anchors.right: removeBtn.left
anchors.verticalCenter: parent.verticalCenter
margin-left: 2
margin-right: 3
Window
id: otuiEditorWindow
size: 360 660
!text: 'OTUI Editor'
anchors.left: parent.left
anchors.top: parent.top
margin-left: 12
margin-top: 40
OtuiSectionLabel
id: pathTitle
!text: '.otui file'
margin-top: 0
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
TextEdit
id: pathEdit
height: 20
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
margin-top: 2
!text: '/game_idle/game_idle.otui'
Button
id: loadBtn
!text: 'Load'
height: 20
width: 100
anchors.top: prev.bottom
anchors.left: parent.left
margin-top: 4
@onClick: modules.dev_otui.loadTarget()
Button
id: browseBtn
!text: 'Open...'
height: 20
width: 100
anchors.top: prev.top
anchors.left: prev.right
margin-left: 4
@onClick: modules.dev_otui.browseFiles()
Button
id: newScreenBtn
!text: 'New screen'
height: 20
anchors.top: prev.top
anchors.left: prev.right
anchors.right: parent.right
margin-left: 4
@onClick: modules.dev_otui.newScreen()
Button
id: reloadBtn
!text: 'Reload'
height: 20
width: 85
anchors.top: prev.bottom
anchors.left: parent.left
margin-top: 4
@onClick: modules.dev_otui.reloadTarget()
Button
id: addElementBtn
!text: '+ Element'
height: 20
width: 80
anchors.top: prev.top
anchors.left: prev.right
margin-left: 4
@onClick: modules.dev_otui.addElement()
Button
id: removeElementBtn
!text: '- Element'
height: 20
width: 80
anchors.top: prev.top
anchors.left: prev.right
margin-left: 4
@onClick: modules.dev_otui.removeElement()
Button
id: closeStageBtn
!text: 'Close'
height: 20
anchors.top: prev.top
anchors.left: prev.right
anchors.right: parent.right
margin-left: 4
@onClick: modules.dev_otui.closeStage()
Button
id: galleryBtn
!text: 'Style gallery'
height: 20
width: 120
anchors.top: prev.bottom
anchors.left: parent.left
margin-top: 4
@onClick: modules.dev_otui.openGallery()
OtuiStateCheck
id: autoReloadCheck
!text: 'Auto-reload'
width: 88
anchors.top: prev.bottom
anchors.left: parent.left
margin-top: 6
@onCheckChange: modules.dev_otui.setAutoReload(self:isChecked())
OtuiStateCheck
id: debugBoxesCheck
!text: 'Boxes'
width: 62
anchors.top: prev.top
anchors.left: prev.right
margin-left: 6
@onCheckChange: modules.dev_otui.setDebugBoxes(self:isChecked())
OtuiStateCheck
id: guidesCheck
!text: 'Guides'
width: 68
anchors.top: prev.top
anchors.left: prev.right
margin-left: 6
@onCheckChange: modules.dev_otui.setGuides(self:isChecked())
OtuiStateCheck
id: pickCheck
!text: 'Edit'
width: 52
anchors.top: prev.top
anchors.left: prev.right
margin-left: 6
@onCheckChange: modules.dev_otui.setPickMode(self:isChecked())
HorizontalSeparator
id: sep1
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
margin-top: 6
OtuiSectionLabel
id: treeTitle
!text: 'Widget tree'
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
TextList
id: treeList
height: 150
anchors.top: treeTitle.bottom
anchors.left: parent.left
anchors.right: treeScroll.left
margin-top: 2
padding: 1
focusable: false
vertical-scrollbar: treeScroll
# anchored to treeTitle, not to treeList: anchoring the two to each other
# creates a circular dependency and the layout is aborted
VerticalScrollBar
id: treeScroll
height: 150
anchors.top: treeTitle.bottom
anchors.right: parent.right
margin-top: 2
step: 14
pixels-scroll: true
Label
id: infoLabel
height: 58
font: verdana-11px-monochrome
color: #a0d0a0
text-wrap: true
text-align: topLeft
phantom: false
anchors.top: treeList.bottom
anchors.left: parent.left
anchors.right: parent.right
margin-top: 4
!text: 'No widget selected.'
OtuiStateCheck
id: stateOn
!text: '$on'
width: 68
anchors.top: prev.bottom
anchors.left: parent.left
margin-top: 2
@onCheckChange: modules.dev_otui.applyState('on', self:isChecked())
OtuiStateCheck
id: stateChecked
!text: '$checked'
width: 78
anchors.top: prev.top
anchors.left: prev.right
margin-left: 4
@onCheckChange: modules.dev_otui.applyState('checked', self:isChecked())
OtuiStateCheck
id: stateDisabled
!text: '$disabled'
width: 78
anchors.top: prev.top
anchors.left: prev.right
margin-left: 4
@onCheckChange: modules.dev_otui.applyState('disabled', self:isChecked())
OtuiStateCheck
id: stateHidden
!text: 'hidden'
width: 68
anchors.top: prev.top
anchors.left: prev.right
margin-left: 4
@onCheckChange: modules.dev_otui.applyState('hidden', self:isChecked())
OtuiSectionLabel
id: propTitle
!text: 'Properties in the file'
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
# From propTitle down the layout is elastic: the property list anchors both
# top AND bottom, so every pixel gained by resizing the window goes to it.
# Without this, growing the window would only create empty space.
TextList
id: propList
anchors.top: propTitle.bottom
anchors.left: parent.left
anchors.right: propScroll.left
anchors.bottom: newPropEdit.top
margin-top: 2
margin-bottom: 4
padding: 1
focusable: true
vertical-scrollbar: propScroll
VerticalScrollBar
id: propScroll
anchors.top: propTitle.bottom
anchors.bottom: newPropEdit.top
anchors.right: parent.right
margin-top: 2
margin-bottom: 4
step: 18
pixels-scroll: true
TextEdit
id: newPropEdit
height: 20
anchors.bottom: applyAllBtn.top
anchors.left: parent.left
anchors.right: addPropBtn.left
margin-bottom: 4
margin-right: 4
!text: ''
Button
id: addPropBtn
!text: 'Add'
height: 20
width: 55
anchors.bottom: applyAllBtn.top
anchors.right: anchorBtn.left
margin-bottom: 4
margin-right: 4
@onClick: modules.dev_otui.addPropertyFromInput()
Button
id: anchorBtn
!text: 'Anchor...'
height: 20
width: 85
anchors.bottom: applyAllBtn.top
anchors.right: parent.right
margin-bottom: 4
@onClick: modules.dev_otui.editAnchors()
Button
id: applyAllBtn
!text: 'Apply (preview)'
height: 20
width: 160
anchors.bottom: statusLabel.top
anchors.left: parent.left
margin-bottom: 4
@onClick: modules.dev_otui.applyAll()
Button
id: saveBtn
!text: 'Save to file'
height: 20
anchors.bottom: statusLabel.top
anchors.left: applyAllBtn.right
anchors.right: parent.right
margin-left: 4
margin-bottom: 4
@onClick: modules.dev_otui.saveFile()
Label
id: statusLabel
font: verdana-11px-monochrome
color: #dfdf7f
text-align: topLeft
text-wrap: true
phantom: false
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
height: 34
!text: 'Ready.'
# negative margins push the borders onto the window frame, outside the
# padding; otherwise they would steal clicks from the scrollbars on the right
ResizeBorder
id: bottomResize
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
margin-bottom: -10
height: 4
minimum: 420
maximum: 1400
ResizeBorder
id: rightResize
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
margin-right: -10
width: 4
minimum: 330
maximum: 1000