diff --git a/modules/client_options/general.otui b/modules/client_options/general.otui index e026986b..f86aa426 100644 --- a/modules/client_options/general.otui +++ b/modules/client_options/general.otui @@ -34,3 +34,7 @@ Panel OptionCheckBox id: enableMusic !text: tr('Enable music') + + OptionCheckBox + id: showLeftPanel + !text: tr('Show left panel') diff --git a/modules/client_options/options.lua b/modules/client_options/options.lua index 47818ece..cd808689 100644 --- a/modules/client_options/options.lua +++ b/modules/client_options/options.lua @@ -14,7 +14,8 @@ local options = { vsync = false, showLevelsInConsole = true, showPrivateMessagesInConsole = false, showPrivateMessagesOnScreen = true, - enableMusic = true } + enableMusic = true, + showLeftPanel = false } local generalPanel local graphicsPanel @@ -129,6 +130,10 @@ function Options.setOption(key, value) addEvent(function() g_sounds.enableMusic(value) end) + elseif key == 'showLeftPanel' then + addEvent(function() + GameInterface.getLeftPanel():setOn(value) + end) end Settings.set(key, value) options[key] = value diff --git a/modules/client_options/options.otui b/modules/client_options/options.otui index 2404ce86..160a18db 100644 --- a/modules/client_options/options.otui +++ b/modules/client_options/options.otui @@ -17,7 +17,7 @@ OptionCheckBox < CheckBox MainWindow id: optionsWindow !text: tr('Options') - size: 350 240 + size: 350 280 @onEnter: Options.hide() @onEscape: Options.hide() diff --git a/modules/client_skins/skins/default/styles/spinboxes.otui b/modules/client_skins/skins/default/styles/spinboxes.otui index 8da463df..0fecee0c 100644 --- a/modules/client_skins/skins/default/styles/spinboxes.otui +++ b/modules/client_skins/skins/default/styles/spinboxes.otui @@ -4,7 +4,7 @@ SpinBox < UISpinBox size: 86 20 text-offset: 0 3 text-margin: 3 - image-source: images/panel_flat.png + image-source: /images/panel_flat.png image-border: 1 $disabled: diff --git a/modules/corelib/widgets/uiminiwindow.lua b/modules/corelib/widgets/uiminiwindow.lua index 4c46f054..371a7e35 100644 --- a/modules/corelib/widgets/uiminiwindow.lua +++ b/modules/corelib/widgets/uiminiwindow.lua @@ -5,9 +5,102 @@ function UIMiniWindow.create() return miniwindow end +function UIMiniWindow:getClassName() + return 'UIMiniWindow' +end + +function UIMiniWindow:open(dontSave) + self:setVisible(true) + + if not dontSave then + self:setSettings({closed = false}) + end + + signalcall(self.onOpen, self) +end + +function UIMiniWindow:close(dontSave) + self:setVisible(false) + + if not dontSave then + self:setSettings({closed = true}) + end + + signalcall(self.onClose, self) +end + +function UIMiniWindow:minimize(dontSave) + self:setOn(true) + self:getChildById('contentsPanel'):hide() + self:getChildById('miniwindowScrollBar'):hide() + self:getChildById('bottomResizeBorder'):hide() + self:getChildById('minimizeButton'):setOn(true) + self.savedHeight = self:getHeight() + self:setHeight(self.minimizedHeight) + + if not dontSave then + self:setSettings({minimized = true}) + end + + signalcall(self.onMinimize, self) +end + +function UIMiniWindow:maximize(dontSave) + self:setOn(false) + self:getChildById('contentsPanel'):show() + self:getChildById('miniwindowScrollBar'):show() + self:getChildById('bottomResizeBorder'):show() + self:getChildById('minimizeButton'):setOn(false) + self:setHeight(self.savedHeight) + + if not dontSave then + self:setSettings({minimized = false}) + end + + signalcall(self.onMaximize, self) +end + function UIMiniWindow:onSetup() - self:getChildById('closeButton').onClick = function() signalcall(self.onClose, self) end - self:getChildById('minimizeButton').onClick = function() signalcall(self.onMinimize, self) end + self:getChildById('closeButton').onClick = + function() + self:close() + end + + self:getChildById('minimizeButton').onClick = + function() + if self:isOn() then + self:maximize() + else + self:minimize() + end + end + + local settings = Settings.getNode('MiniWindows') + if settings then + local selfSettings = settings[self:getId()] + if selfSettings then + if selfSettings.parentId then + local parent = rootWidget:recursiveGetChildById(selfSettings.parentId) + if parent then + if parent:getClassName() == 'UIMiniWindowContainer' and selfSettings.index and parent:isOn() then + parent:scheduleInsert(self, selfSettings.index) + elseif selfSettings.position then + self:setParent(parent) + self:setPosition(topoint(selfSettings.position)) + self:bindRectToParent() + end + end + end + + if selfSettings.minimized then + self:minimize(true) + end + + if selfSettings.closed then + self:close(true) + end + end + end end function UIMiniWindow:onDragEnter(mousePos) @@ -18,6 +111,7 @@ function UIMiniWindow:onDragEnter(mousePos) local containerParent = parent:getParent() parent:removeChild(self) containerParent:addChild(self) + parent:saveChildren() end local oldPos = self:getPosition() @@ -84,6 +178,15 @@ function UIMiniWindow:onDragLeave(droppedWidget, mousePos) self.setMovedChildMargin = nil self.movedIndex = nil end + + local parent = self:getParent() + if parent then + if parent:getClassName() == 'UIMiniWindowContainer' then + parent:saveChildren() + else + self:saveParentPosition(parent:getId(), self:getPosition()) + end + end end function UIMiniWindow:onFocusChange(focused) @@ -95,28 +198,34 @@ function UIMiniWindow:onFocusChange(focused) end end -function UIMiniWindow:onClose() -end +function UIMiniWindow:setSettings(data) + local settings = Settings.getNode('MiniWindows') + if not settings then + settings = {} + end + + local id = self:getId() + if not settings[id] then + settings[id] = {} + end -function UIMiniWindow:onMinimize() - if self:isOn() then - self:setOn(false) - self:getChildById('contentsPanel'):show() - self:getChildById('miniwindowScrollBar'):show() - self:getChildById('bottomResizeBorder'):show() - self:getChildById('minimizeButton'):setOn(false) - self:setHeight(self.savedHeight) - else - self.savedHeight = self:getHeight() - self:setHeight(self.minimizedHeight) - self:setOn(true) - self:getChildById('contentsPanel'):hide() - self:getChildById('miniwindowScrollBar'):hide() - self:getChildById('bottomResizeBorder'):hide() - self:getChildById('minimizeButton'):setOn(true) + for key,value in pairs(data) do + settings[id][key] = value end + + Settings.setNode('MiniWindows', settings) end -function UIMiniWindow:getClassName() - return 'UIMiniWindow' +function UIMiniWindow:saveParentPosition(parentId, position) + local selfSettings = {} + selfSettings.parentId = parentId + selfSettings.position = pointtostring(position) + self:setSettings(selfSettings) +end + +function UIMiniWindow:saveParentIndex(parentId, index) + local selfSettings = {} + selfSettings.parentId = parentId + selfSettings.index = index + self:setSettings(selfSettings) end diff --git a/modules/corelib/widgets/uiminiwindowcontainer.lua b/modules/corelib/widgets/uiminiwindowcontainer.lua index a68b61ff..074ebbde 100644 --- a/modules/corelib/widgets/uiminiwindowcontainer.lua +++ b/modules/corelib/widgets/uiminiwindowcontainer.lua @@ -2,6 +2,7 @@ UIMiniWindowContainer = extends(UIWidget) function UIMiniWindowContainer.create() local container = UIMiniWindowContainer.internalCreate() + container.scheduledWidgets = {} container:setFocusable(false) container:setPhantom(true) return container @@ -14,7 +15,9 @@ function UIMiniWindowContainer:onDrop(widget, mousePos) return true end - oldParent:removeChild(widget) + if oldParent then + oldParent:removeChild(widget) + end if widget.movedWidget then local index = self:getChildIndex(widget.movedWidget) @@ -27,6 +30,56 @@ function UIMiniWindowContainer:onDrop(widget, mousePos) end end +function UIMiniWindowContainer:swapInsert(widget, index) + local oldParent = widget:getParent() + local oldIndex = self:getChildIndex(widget) + + if oldParent == self and oldIndex ~= index then + local oldWidget = self:getChildByIndex(index) + self:removeChild(oldWidget) + self:insertChild(oldIndex, oldWidget) + self:removeChild(widget) + self:insertChild(index, widget) + end +end + +function UIMiniWindowContainer:scheduleInsert(widget, index) + if index - 1 > self:getChildCount() then + if self.scheduledWidgets[index] then + warning('replacing scheduled widget id ' .. widget:getId()) + end + self.scheduledWidgets[index] = widget + else + local oldParent = widget:getParent() + if oldParent ~= self then + oldParent:removeChild(widget) + self:insertChild(index, widget) + else + self:swapInsert(widget, index) + end + + while true do + local placed = false + for nIndex,nWidget in pairs(self.scheduledWidgets) do + if nIndex - 1 <= self:getChildCount() then + self:insertChild(nIndex, nWidget) + self.scheduledWidgets[nIndex] = nil + placed = true + break + end + end + if not placed then break end + end + end +end + +function UIMiniWindowContainer:saveChildren() + local children = self:getChildren() + for i=1,#children do + children[i]:saveParentIndex(self:getId(), i) + end +end + function UIMiniWindowContainer:getClassName() return 'UIMiniWindowContainer' end diff --git a/modules/corelib/widgets/uiresizeborder.lua b/modules/corelib/widgets/uiresizeborder.lua index cece93b1..b73232d4 100644 --- a/modules/corelib/widgets/uiresizeborder.lua +++ b/modules/corelib/widgets/uiresizeborder.lua @@ -80,12 +80,24 @@ function UIResizeBorder:onStyleApply(styleName, styleNode) end end +function UIResizeBorder:onVisibilityChange(visible) + if visible and self.maximum == self.minimum then + self:hide() + end +end + function UIResizeBorder:setMaximum(maximum) self.maximum = maximum + if self.maximum == self.minimum then + self:hide() + end end function UIResizeBorder:setMinimum(minimum) self.minimum = minimum + if self.maximum == self.minimum then + self:hide() + end end function UIResizeBorder:getMaximum() return self.maximum end diff --git a/modules/game_battle/battle.lua b/modules/game_battle/battle.lua index 6f164a5f..0ab7c87d 100644 --- a/modules/game_battle/battle.lua +++ b/modules/game_battle/battle.lua @@ -36,7 +36,7 @@ table.insert(lifeBarColors, {percentAbove = -1, color = '#4F0000' } ) -- public functions function Battle.init() - battleWindow = displayUI('battle.otui', GameInterface.getLeftPanel()) + battleWindow = displayUI('battle.otui', GameInterface.getRightPanel()) battleButton = TopMenu.addGameToggleButton('battleButton', tr('Battle') .. ' (Ctrl+B)', 'battle.png', Battle.toggle) battleButton:setOn(true) Keyboard.bindKeyDown('Ctrl+B', Battle.toggle) @@ -93,9 +93,17 @@ function Battle.terminate() end function Battle.toggle() - local visible = not battleWindow:isExplicitlyVisible() - battleWindow:setVisible(visible) - battleButton:setOn(visible) + if battleButton:isOn() then + battleWindow:close() + battleButton:setOn(false) + else + battleWindow:open() + battleButton:setOn(true) + end +end + +function Battle.onMiniWindowClose() + battleButton:setOn(false) end function Battle.addAllCreatures() diff --git a/modules/game_battle/battle.otui b/modules/game_battle/battle.otui index 74ab3114..8a6853df 100644 --- a/modules/game_battle/battle.otui +++ b/modules/game_battle/battle.otui @@ -41,7 +41,7 @@ MiniWindow !text: tr('Battle') height: 166 icon: battle.png - @onClose: Battle.toggle() + @onClose: Battle.onMiniWindowClose() MiniWindowContents BattlePlayers diff --git a/modules/game_combatcontrols/combatcontrols.lua b/modules/game_combatcontrols/combatcontrols.lua index 27fe569f..0dec2380 100644 --- a/modules/game_combatcontrols/combatcontrols.lua +++ b/modules/game_combatcontrols/combatcontrols.lua @@ -129,8 +129,15 @@ function CombatControls.offline() end function CombatControls.toggle() - local visible = not combatControlsWindow:isExplicitlyVisible() - combatControlsWindow:setVisible(visible) - combatControlsButton:setOn(visible) + if combatControlsButton:isOn() then + combatControlsWindow:close() + combatControlsButton:setOn(false) + else + combatControlsWindow:open() + combatControlsButton:setOn(true) + end end +function CombatControls.onMiniWindowClose() + combatControlsButton:setOn(false) +end diff --git a/modules/game_combatcontrols/combatcontrols.otui b/modules/game_combatcontrols/combatcontrols.otui index 4179754f..86a51d07 100644 --- a/modules/game_combatcontrols/combatcontrols.otui +++ b/modules/game_combatcontrols/combatcontrols.otui @@ -20,10 +20,11 @@ SafeFightBox < CombatBox image-source: /game_combatcontrols/icons/safefight.png MiniWindow + id: combatControlsWindow !text: tr('Combat Controls') icon: combatcontrols.png height: 64 - @onClose: CombatControls.toggle() + @onClose: CombatControls.onMiniWindowClose() MiniWindowContents FightOffensiveBox diff --git a/modules/game_healthbar/healthbar.lua b/modules/game_healthbar/healthbar.lua index bee3ad6e..83087ae1 100644 --- a/modules/game_healthbar/healthbar.lua +++ b/modules/game_healthbar/healthbar.lua @@ -35,7 +35,7 @@ function HealthBar.init() connect(g_game, { onGameEnd = HealthBar.offline }) - healthBarWindow = displayUI('healthbar.otui', GameInterface.getLeftPanel()) + healthBarWindow = displayUI('healthbar.otui', GameInterface.getRightPanel()) healthBarButton = TopMenu.addGameToggleButton('healthBarButton', tr('Health Bar'), 'healthbar.png', HealthBar.toggle) healthBarButton:setOn(true) healthBar = healthBarWindow:recursiveGetChildById('healthBar') @@ -71,9 +71,17 @@ function HealthBar.terminate() end function HealthBar.toggle() - local visible = not healthBarWindow:isExplicitlyVisible() - healthBarWindow:setVisible(visible) - healthBarButton:setOn(visible) + if healthBarButton:isOn() then + healthBarWindow:close() + healthBarButton:setOn(false) + else + healthBarWindow:open() + healthBarButton:setOn(true) + end +end + +function HealthBar.onMiniWindowClose() + healthBarButton:setOn(false) end function HealthBar.offline() diff --git a/modules/game_healthbar/healthbar.otui b/modules/game_healthbar/healthbar.otui index 81567b04..8117c00d 100644 --- a/modules/game_healthbar/healthbar.otui +++ b/modules/game_healthbar/healthbar.otui @@ -45,7 +45,7 @@ MiniWindow id: healthBarWindow !text: tr('Health Bar') height: 86 - @onClose: HealthBar.toggle() + @onClose: HealthBar.onMiniWindowClose() MiniWindowContents HealthBar @@ -61,4 +61,3 @@ MiniWindow margin-top: 5 anchors.top: prev.bottom anchors.horizontalcenter: parent.horizontalcenter - diff --git a/modules/game_interface/gameinterface.lua b/modules/game_interface/gameinterface.lua index faf69a16..c850779b 100644 --- a/modules/game_interface/gameinterface.lua +++ b/modules/game_interface/gameinterface.lua @@ -9,6 +9,15 @@ local gameBottomPanel local logoutButton local mouseGrabberWidget +local function onLeftPanelVisibilityChange(leftPanel, visible) + if not visible then + local children = leftPanel:getChildren() + for i=1,#children do + children[i]:setParent(gameRightPanel) + end + end +end + function GameInterface.init() connect(g_game, { onGameStart = GameInterface.show }, true) connect(g_game, { onGameEnd = GameInterface.hide }, true) @@ -24,6 +33,7 @@ function GameInterface.init() gameRightPanel = gameRootPanel:getChildById('gameRightPanel') gameLeftPanel = gameRootPanel:getChildById('gameLeftPanel') gameBottomPanel = gameRootPanel:getChildById('gameBottomPanel') + connect(gameLeftPanel, { onVisibilityChange = onLeftPanelVisibilityChange }) logoutButton = TopMenu.addRightButton('logoutButton', 'Logout', '/images/logout.png', GameInterface.tryLogout) logoutButton:hide() @@ -63,6 +73,7 @@ end function GameInterface.terminate() disconnect(g_game, { onGameStart = GameInterface.show }) disconnect(g_game, { onGameEnd = GameInterface.hide }) + disconnect(gameLeftPanel, { onVisibilityChange = onLeftPanelVisibilityChange }) logoutButton:destroy() logoutButton = nil diff --git a/modules/game_interface/gameinterface.otui b/modules/game_interface/gameinterface.otui index 3ebd5213..31209acb 100644 --- a/modules/game_interface/gameinterface.otui +++ b/modules/game_interface/gameinterface.otui @@ -40,6 +40,11 @@ UIWidget anchors.top: parent.top anchors.bottom: parent.bottom focusable: false + visible: true + on: true + $!on: + width: 0 + visible: false GameSidePanel id: gameRightPanel @@ -49,6 +54,7 @@ UIWidget anchors.top: parent.top anchors.bottom: parent.bottom focusable: false + on: true Splitter id: bottomSplitter diff --git a/modules/game_inventory/inventory.lua b/modules/game_inventory/inventory.lua index 9533d9fe..70604a41 100644 --- a/modules/game_inventory/inventory.lua +++ b/modules/game_inventory/inventory.lua @@ -14,7 +14,6 @@ function Inventory.init() Keyboard.bindKeyDown('Ctrl+I', Inventory.toggle) inventoryWindow = displayUI('inventory.otui', GameInterface.getRightPanel()) - inventoryWindow.onClose = Inventory.toggle inventoryPanel = inventoryWindow:getChildById('contentsPanel') inventoryButton = TopMenu.addGameToggleButton('inventoryButton', tr('Inventory') .. ' (Ctrl+I)', 'inventory.png', Inventory.toggle) inventoryButton:setOn(true) @@ -50,9 +49,17 @@ function Inventory.refresh() end function Inventory.toggle() - local visible = not inventoryWindow:isExplicitlyVisible() - inventoryWindow:setVisible(visible) - inventoryButton:setOn(visible) + if inventoryButton:isOn() then + inventoryWindow:close() + inventoryButton:setOn(false) + else + inventoryWindow:open() + inventoryButton:setOn(true) + end +end + +function Inventory.onMiniWindowClose() + inventoryButton:setOn(false) end -- hooked events diff --git a/modules/game_inventory/inventory.otui b/modules/game_inventory/inventory.otui index 51b7afa6..11ec1685 100644 --- a/modules/game_inventory/inventory.otui +++ b/modules/game_inventory/inventory.otui @@ -1,9 +1,9 @@ MiniWindow - id: inventoryMiniWindow + id: inventoryWindow !text: tr('Inventory') icon: inventory.png height: 180 - @onClose: Inventory.toggle() + @onClose: Inventory.onMiniWindowClose() MiniWindowContents Item diff --git a/modules/game_skills/skills.lua b/modules/game_skills/skills.lua index b3816fd7..15cc1991 100644 --- a/modules/game_skills/skills.lua +++ b/modules/game_skills/skills.lua @@ -35,7 +35,7 @@ function Skills.init() onSkillChange = Skills.onSkillChange }) - skillsWindow = displayUI('skills.otui', GameInterface.getLeftPanel()) + skillsWindow = displayUI('skills.otui', GameInterface.getRightPanel()) skillsButton = TopMenu.addGameToggleButton('skillsButton', tr('Skills') .. ' (Ctrl+S)', 'skills.png', Skills.toggle) skillsButton:setOn(true) Keyboard.bindKeyDown('Ctrl+S', Skills.toggle) @@ -84,9 +84,17 @@ function Skills.refresh() end function Skills.toggle() - local visible = not skillsWindow:isExplicitlyVisible() - skillsWindow:setVisible(visible) - skillsButton:setOn(visible) + if skillsButton:isOn() then + skillsWindow:close() + skillsButton:setOn(false) + else + skillsWindow:open() + skillsButton:setOn(true) + end +end + +function Skills.onMiniWindowClose() + skillsButton:setOn(false) end function Skills.onSkillButtonClick(button) diff --git a/modules/game_skills/skills.otui b/modules/game_skills/skills.otui index 664d74d5..9b151741 100644 --- a/modules/game_skills/skills.otui +++ b/modules/game_skills/skills.otui @@ -33,9 +33,9 @@ SkillPercentPanel < ProgressBar MiniWindow id: skillWindow !text: tr('Skills') - height: 350 + height: 150 icon: skills.png - @onClose: Skills.toggle() + @onClose: Skills.onMiniWindowClose() MiniWindowContents anchors.fill: parent diff --git a/modules/game_viplist/viplist.lua b/modules/game_viplist/viplist.lua index c69c3b0e..c110d586 100644 --- a/modules/game_viplist/viplist.lua +++ b/modules/game_viplist/viplist.lua @@ -14,7 +14,7 @@ function VipList.init() Keyboard.bindKeyDown('Ctrl+P', VipList.toggle) - vipWindow = displayUI('viplist.otui', GameInterface.getLeftPanel()) + vipWindow = displayUI('viplist.otui', GameInterface.getRightPanel()) vipButton = TopMenu.addGameToggleButton('vipListButton', tr('VIP list') .. ' (Ctrl+P)', 'viplist.png', VipList.toggle) vipButton:setOn(true) @@ -48,9 +48,17 @@ function VipList.clear() end function VipList.toggle() - local visible = not vipWindow:isExplicitlyVisible() - vipWindow:setVisible(visible) - vipButton:setOn(visible) + if vipButton:isOn() then + vipWindow:close() + vipButton:setOn(false) + else + vipWindow:open() + vipButton:setOn(true) + end +end + +function VipList.onMiniWindowClose() + vipButton:setOn(false) end function VipList.createAddWindow() diff --git a/modules/game_viplist/viplist.otui b/modules/game_viplist/viplist.otui index 61da55ae..359707bd 100644 --- a/modules/game_viplist/viplist.otui +++ b/modules/game_viplist/viplist.otui @@ -8,7 +8,7 @@ MiniWindow !text: tr('VIP List') height: 100 icon: viplist.png - @onClose: VipList.toggle() + @onClose: VipList.onMiniWindowClose() MiniWindowContents layout: verticalBox diff --git a/src/framework/ui/uiwidget.cpp b/src/framework/ui/uiwidget.cpp index bea52718..6fb728ec 100644 --- a/src/framework/ui/uiwidget.cpp +++ b/src/framework/ui/uiwidget.cpp @@ -375,6 +375,11 @@ void UIWidget::moveChildToIndex(const UIWidgetPtr& child, int index) if(!child) return; + if((uint)index - 1 > m_children.size()) { + g_logger.error(stdext::format("moving %s to index %d on %s", child->getId(), index, m_id)); + return; + } + // remove and push child again auto it = std::find(m_children.begin(), m_children.end(), child); if(it == m_children.end()) { @@ -384,6 +389,7 @@ void UIWidget::moveChildToIndex(const UIWidgetPtr& child, int index) m_children.erase(it); m_children.insert(m_children.begin() + index - 1, child); updateChildrenIndexStates(); + updateLayout(); } void UIWidget::lockChild(const UIWidgetPtr& child) @@ -751,7 +757,10 @@ void UIWidget::destroyChildren() void UIWidget::setId(const std::string& id) { - m_id = id; + if(id != m_id) { + m_id = id; + callLuaField("onIdChange", id); + } } void UIWidget::setParent(const UIWidgetPtr& parent) @@ -885,6 +894,8 @@ void UIWidget::setVisible(bool visible) g_ui.onWidgetAppear(asUIWidget()); else g_ui.onWidgetDisappear(asUIWidget()); + + callLuaField("onVisibilityChange", visible); } }