Ui minipanel refactor (#926)

* d2player/hud: Make minipanel button a real ui/button

* d2ui/button: Add implicit tooltips

for now it is only for close buttons.

* d2ui/frame: Add size caluclation

now frame.GetSize() returns meaningful values.

* d2ui/button: Add minipanel button types

* d2ui/hero_stats_panel: Fix cached image being way to big

* d2ui/widget_group: Fix widget groups size calculation

* d2ui/widget_group: Add debug rendering

* d2ui/widget_group: SetVisible() now sets the visibility of the group object

* d2player: Refactor mini_panel

we converted all elements to widgets. Thus rendering from game_controls
is no longer neccessary.

* d2ui/button: Add disabled color to layouts

* d2player/gamecontrols: temp hide minipanel when in esc menu

* d2ui/widget_group: Add OffsetPosition() method

* d2player/mini_panel: Implement moving of minipanel

this only occours when other panels are opened.

* d2player/minipanel: Fix inv/skilltree/char closebuttons

these would screw up the moving of the mini panel.

* Fix linter

* d2player/minipanel: Add tooltips to buttons

* d2player/skilltree: Fix icon rendering
This commit is contained in:
juander-ux 2020-11-16 10:41:01 +01:00 committed by GitHub
parent 12821147ce
commit ba5ea334cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 661 additions and 319 deletions

View File

@ -40,6 +40,8 @@ const (
ButtonTypeMinipanelMen ButtonType = 19
ButtonTypeSquareClose ButtonType = 20
ButtonTypeSkillTreeTab ButtonType = 21
ButtonTypeMinipanelOpenClose ButtonType = 22
ButtonTypeMinipanelParty ButtonType = 23
ButtonNoFixedWidth int = -1
ButtonNoFixedHeight int = -1
@ -71,6 +73,7 @@ type ButtonLayout struct {
YSegments int
BaseFrame int
DisabledFrame int
DisabledColor uint32
TextOffset int
FixedWidth int
FixedHeight int
@ -78,8 +81,21 @@ type ButtonLayout struct {
Toggleable bool
AllowFrameChange bool
HasImage bool
Tooltip int
TooltipXOffset int
TooltipYOffset int
}
const (
buttonTooltipNone int = iota
buttonTooltipClose
)
const (
buttonCloseTooltipXOffset = 15
buttonCloseTooltipYOffset = -2
)
const (
buttonWideSegmentsX = 2
buttonWideSegmentsY = 1
@ -106,13 +122,28 @@ const (
buttonBuySellSegmentsY = 1
buttonBuySellDisabledFrame = 1
buttonSkillTreeTabXSegments = 1
buttonSkillTreeTabYSegments = 1
buttonSkillTreeTabXSegments = 1
buttonSkillTreeTabYSegments = 1
buttonSkillTreeTabDisabledFrame = 7
buttonSkillTreeTabBaseFrame = 7
buttonSkillTreeTabFixedWidth = 93
buttonSkillTreeTabFixedHeight = 107
buttonMinipanelOpenCloseBaseFrame = 0
buttonMinipanelDisabledFrame = 2
buttonMinipanelXSegments = 1
buttonMinipanelYSegments = 1
buttonMinipanelCharacterBaseFrame = 0
buttonMinipanelInventoryBaseFrame = 2
buttonMinipanelSkilltreeBaseFrame = 4
buttonMinipanelPartyBaseFrame = 6
buttonMinipanelAutomapBaseFrame = 8
buttonMinipanelMessageBaseFrame = 10
buttonMinipanelQuestBaseFrame = 12
buttonMinipanelMenBaseFrame = 14
buttonRunSegmentsX = 1
buttonRunSegmentsY = 1
buttonRunDisabledFrame = -1
@ -127,6 +158,7 @@ func getButtonLayouts() map[ButtonType]ButtonLayout {
XSegments: buttonWideSegmentsX,
YSegments: buttonWideSegmentsY,
DisabledFrame: buttonWideDisabledFrame,
DisabledColor: lightGreyAlpha75,
TextOffset: buttonWideTextOffset,
ResourceName: d2resource.WideButtonBlank,
PaletteName: d2resource.PaletteUnits,
@ -141,6 +173,7 @@ func getButtonLayouts() map[ButtonType]ButtonLayout {
XSegments: buttonShortSegmentsX,
YSegments: buttonShortSegmentsY,
DisabledFrame: buttonShortDisabledFrame,
DisabledColor: lightGreyAlpha75,
TextOffset: buttonShortTextOffset,
ResourceName: d2resource.ShortButtonBlank,
PaletteName: d2resource.PaletteUnits,
@ -154,6 +187,7 @@ func getButtonLayouts() map[ButtonType]ButtonLayout {
ButtonTypeMedium: {
XSegments: buttonMediumSegmentsX,
YSegments: buttonMediumSegmentsY,
DisabledColor: lightGreyAlpha75,
ResourceName: d2resource.MediumButtonBlank,
PaletteName: d2resource.PaletteUnits,
FontPath: d2resource.FontExocet10,
@ -167,6 +201,7 @@ func getButtonLayouts() map[ButtonType]ButtonLayout {
XSegments: buttonTallSegmentsX,
YSegments: buttonTallSegmentsY,
TextOffset: buttonTallTextOffset,
DisabledColor: lightGreyAlpha75,
ResourceName: d2resource.TallButtonBlank,
PaletteName: d2resource.PaletteUnits,
FontPath: d2resource.FontExocet10,
@ -180,6 +215,7 @@ func getButtonLayouts() map[ButtonType]ButtonLayout {
XSegments: buttonOkCancelSegmentsX,
YSegments: buttonOkCancelSegmentsY,
DisabledFrame: buttonOkCancelDisabledFrame,
DisabledColor: lightGreyAlpha75,
ResourceName: d2resource.CancelButton,
PaletteName: d2resource.PaletteUnits,
FontPath: d2resource.FontRediculous,
@ -193,6 +229,7 @@ func getButtonLayouts() map[ButtonType]ButtonLayout {
XSegments: buttonRunSegmentsX,
YSegments: buttonRunSegmentsY,
DisabledFrame: buttonRunDisabledFrame,
DisabledColor: lightGreyAlpha75,
ResourceName: d2resource.RunButton,
PaletteName: d2resource.PaletteSky,
Toggleable: true,
@ -207,6 +244,7 @@ func getButtonLayouts() map[ButtonType]ButtonLayout {
XSegments: buttonBuySellSegmentsX,
YSegments: buttonBuySellSegmentsY,
DisabledFrame: buttonBuySellDisabledFrame,
DisabledColor: lightGreyAlpha75,
ResourceName: d2resource.BuySellButton,
PaletteName: d2resource.PaletteUnits,
Toggleable: true,
@ -217,11 +255,15 @@ func getButtonLayouts() map[ButtonType]ButtonLayout {
FixedWidth: ButtonNoFixedWidth,
FixedHeight: ButtonNoFixedHeight,
LabelColor: greyAlpha100,
Tooltip: buttonTooltipClose,
TooltipXOffset: buttonCloseTooltipXOffset,
TooltipYOffset: buttonCloseTooltipYOffset,
},
ButtonTypeSkillTreeTab: {
XSegments: buttonSkillTreeTabXSegments,
YSegments: buttonSkillTreeTabYSegments,
DisabledFrame: buttonSkillTreeTabDisabledFrame,
DisabledColor: lightGreyAlpha75,
BaseFrame: buttonSkillTreeTabBaseFrame,
ResourceName: d2resource.SkillsPanelAmazon,
PaletteName: d2resource.PaletteSky,
@ -233,6 +275,134 @@ func getButtonLayouts() map[ButtonType]ButtonLayout {
FixedHeight: buttonSkillTreeTabFixedHeight,
LabelColor: whiteAlpha100,
},
ButtonTypeMinipanelOpenClose: {
XSegments: buttonMinipanelXSegments,
YSegments: buttonMinipanelYSegments,
DisabledFrame: buttonMinipanelDisabledFrame,
DisabledColor: whiteAlpha100,
BaseFrame: buttonMinipanelOpenCloseBaseFrame,
ResourceName: d2resource.MenuButton,
PaletteName: d2resource.PaletteSky,
Toggleable: true,
FontPath: d2resource.Font16,
AllowFrameChange: true,
HasImage: true,
FixedWidth: ButtonNoFixedWidth,
FixedHeight: ButtonNoFixedHeight,
LabelColor: whiteAlpha100,
},
ButtonTypeMinipanelCharacter: {
XSegments: buttonMinipanelXSegments,
YSegments: buttonMinipanelYSegments,
BaseFrame: buttonMinipanelCharacterBaseFrame,
ResourceName: d2resource.MinipanelButton,
PaletteName: d2resource.PaletteSky,
Toggleable: false,
FontPath: d2resource.Font16,
AllowFrameChange: true,
HasImage: true,
FixedWidth: ButtonNoFixedWidth,
FixedHeight: ButtonNoFixedHeight,
LabelColor: whiteAlpha100,
},
ButtonTypeMinipanelInventory: {
XSegments: buttonMinipanelXSegments,
YSegments: buttonMinipanelYSegments,
BaseFrame: buttonMinipanelInventoryBaseFrame,
ResourceName: d2resource.MinipanelButton,
PaletteName: d2resource.PaletteSky,
Toggleable: false,
FontPath: d2resource.Font16,
AllowFrameChange: true,
HasImage: true,
FixedWidth: ButtonNoFixedWidth,
FixedHeight: ButtonNoFixedHeight,
LabelColor: whiteAlpha100,
},
ButtonTypeMinipanelSkill: {
XSegments: buttonMinipanelXSegments,
YSegments: buttonMinipanelYSegments,
BaseFrame: buttonMinipanelSkilltreeBaseFrame,
ResourceName: d2resource.MinipanelButton,
PaletteName: d2resource.PaletteSky,
Toggleable: false,
FontPath: d2resource.Font16,
AllowFrameChange: true,
HasImage: true,
FixedWidth: ButtonNoFixedWidth,
FixedHeight: ButtonNoFixedHeight,
LabelColor: whiteAlpha100,
},
ButtonTypeMinipanelParty: {
XSegments: buttonMinipanelXSegments,
YSegments: buttonMinipanelYSegments,
BaseFrame: buttonMinipanelPartyBaseFrame,
ResourceName: d2resource.MinipanelButton,
PaletteName: d2resource.PaletteSky,
Toggleable: false,
FontPath: d2resource.Font16,
AllowFrameChange: true,
HasImage: true,
FixedWidth: ButtonNoFixedWidth,
FixedHeight: ButtonNoFixedHeight,
LabelColor: whiteAlpha100,
},
ButtonTypeMinipanelAutomap: {
XSegments: buttonMinipanelXSegments,
YSegments: buttonMinipanelYSegments,
BaseFrame: buttonMinipanelAutomapBaseFrame,
ResourceName: d2resource.MinipanelButton,
PaletteName: d2resource.PaletteSky,
Toggleable: false,
FontPath: d2resource.Font16,
AllowFrameChange: true,
HasImage: true,
FixedWidth: ButtonNoFixedWidth,
FixedHeight: ButtonNoFixedHeight,
LabelColor: whiteAlpha100,
},
ButtonTypeMinipanelMessage: {
XSegments: buttonMinipanelXSegments,
YSegments: buttonMinipanelYSegments,
BaseFrame: buttonMinipanelMessageBaseFrame,
ResourceName: d2resource.MinipanelButton,
PaletteName: d2resource.PaletteSky,
Toggleable: false,
FontPath: d2resource.Font16,
AllowFrameChange: true,
HasImage: true,
FixedWidth: ButtonNoFixedWidth,
FixedHeight: ButtonNoFixedHeight,
LabelColor: whiteAlpha100,
},
ButtonTypeMinipanelQuest: {
XSegments: buttonMinipanelXSegments,
YSegments: buttonMinipanelYSegments,
BaseFrame: buttonMinipanelQuestBaseFrame,
ResourceName: d2resource.MinipanelButton,
PaletteName: d2resource.PaletteSky,
Toggleable: false,
FontPath: d2resource.Font16,
AllowFrameChange: true,
HasImage: true,
FixedWidth: ButtonNoFixedWidth,
FixedHeight: ButtonNoFixedHeight,
LabelColor: whiteAlpha100,
},
ButtonTypeMinipanelMen: {
XSegments: buttonMinipanelXSegments,
YSegments: buttonMinipanelYSegments,
BaseFrame: buttonMinipanelMenBaseFrame,
ResourceName: d2resource.MinipanelButton,
PaletteName: d2resource.PaletteSky,
Toggleable: false,
FontPath: d2resource.Font16,
AllowFrameChange: true,
HasImage: true,
FixedWidth: ButtonNoFixedWidth,
FixedHeight: ButtonNoFixedHeight,
LabelColor: whiteAlpha100,
},
}
}
@ -251,6 +421,7 @@ type Button struct {
enabled bool
pressed bool
toggled bool
tooltip *Tooltip
}
// NewButton creates an instance of Button
@ -311,6 +482,8 @@ func (ui *UIManager) NewButton(buttonType ButtonType, text string) *Button {
buttonSprite.SetPosition(0, 0)
buttonSprite.SetEffect(d2enum.DrawEffectModulate)
btn.createTooltip()
ui.addWidget(btn) // important that this comes before prerenderStates!
btn.prerenderStates(buttonSprite, &buttonLayout, lbl)
@ -325,6 +498,21 @@ type buttonStateDescriptor struct {
fmtErr string
}
func (v *Button) createTooltip() {
var t *Tooltip
switch v.buttonLayout.Tooltip {
case buttonTooltipNone:
return
case buttonTooltipClose:
t = v.manager.NewTooltip(d2resource.Font16, d2resource.PaletteSky, TooltipXCenter, TooltipYBottom)
t.SetText(v.manager.asset.TranslateString("strClose"))
}
t.SetVisible(false)
v.SetTooltip(t)
}
func (v *Button) prerenderStates(btnSprite *Sprite, btnLayout *ButtonLayout, label *Label) {
numButtonStates := btnSprite.GetFrameCount() / (btnLayout.XSegments * btnLayout.YSegments)
@ -438,7 +626,7 @@ func (v *Button) Render(target d2interface.Surface) {
switch {
case !v.enabled:
target.PushColor(d2util.Color(lightGreyAlpha75))
target.PushColor(d2util.Color(v.buttonLayout.DisabledColor))
defer target.Pop()
target.Render(v.disabledSurface)
case v.toggled && v.pressed:
@ -486,6 +674,32 @@ func (v *Button) SetPressed(pressed bool) {
v.pressed = pressed
}
// SetVisible sets the pressed state of the button
func (v *Button) SetVisible(visible bool) {
v.BaseWidget.SetVisible(visible)
if v.isHovered() && !visible {
v.hoverEnd()
}
}
// SetPosition sets the position of the widget
func (v *Button) SetPosition(x, y int) {
v.BaseWidget.SetPosition(x, y)
if v.buttonLayout.Tooltip != buttonTooltipNone {
v.tooltip.SetPosition(x+v.buttonLayout.TooltipXOffset, y+v.buttonLayout.TooltipYOffset)
}
}
// SetTooltip adds a tooltip to the button
func (v *Button) SetTooltip(t *Tooltip) {
v.tooltip = t
v.manager.addWidget(t)
v.OnHoverStart(func() { log.Print("HoverStart"); v.tooltip.SetVisible(true) })
v.OnHoverEnd(func() { v.tooltip.SetVisible(false) })
}
func half(n int) int {
return n / 2
}

View File

@ -32,7 +32,7 @@ type UIFrame struct {
const (
leftFrameTopLeft = iota
leftFrameTopRight
leftFrameMiddleRight
leftFrameMiddleLeft
leftFrameBottomLeft
leftFrameBottomRight
rightFrameTopLeft
@ -80,6 +80,51 @@ func (u *UIFrame) Load() {
}
u.frame = sprite
u.calculateSize()
}
func (u *UIFrame) calculateSize() {
var framesWidth, framesHeight []int
if u.frameOrientation == FrameLeft {
framesWidth = []int{
leftFrameTopLeft,
leftFrameTopRight,
}
framesHeight = []int{
leftFrameTopLeft,
leftFrameMiddleLeft,
leftFrameBottomLeft,
}
} else if u.frameOrientation == FrameRight {
framesWidth = []int{
rightFrameTopLeft,
rightFrameTopRight,
}
framesHeight = []int{
rightFrameTopRight,
rightFrameMiddleRight,
rightFrameBottomRight,
}
}
for i := range framesWidth {
w, _, err := u.frame.GetFrameSize(framesWidth[i])
if err != nil {
log.Print(err)
}
u.width += w
}
for i := range framesHeight {
_, h, err := u.frame.GetFrameSize(framesHeight[i])
if err != nil {
log.Print(err)
}
u.height += h
}
}
// Render the frame to the target surface
@ -101,7 +146,7 @@ func (u *UIFrame) renderLeft(target d2interface.Surface) error {
framePieces := []int{
leftFrameTopLeft,
leftFrameTopRight,
leftFrameMiddleRight,
leftFrameMiddleLeft,
leftFrameBottomLeft,
leftFrameBottomRight,
}
@ -129,7 +174,7 @@ func (u *UIFrame) renderLeft(target d2interface.Surface) error {
case leftFrameTopRight:
c.x, c.y = currentX, startY+height
currentX = startX
case leftFrameMiddleRight:
case leftFrameMiddleLeft:
c.x, c.y = currentX, currentY+height
currentY += height
case leftFrameBottomLeft:

View File

@ -105,6 +105,12 @@ func (ui *UIManager) OnMouseMove(event d2interface.MouseMoveEvent) bool {
}
}
for _, w := range ui.widgets {
if w.GetVisible() {
w.OnMouseMove(event.X(), event.Y())
}
}
return false
}
@ -134,17 +140,17 @@ func (ui *UIManager) OnMouseButtonDown(event d2interface.MouseEvent) bool {
// Render renders all of the UI elements
func (ui *UIManager) Render(target d2interface.Surface) {
for _, widget := range ui.widgets {
if widget.GetVisible() {
widget.Render(target)
}
}
for _, widgetGroup := range ui.widgetsGroups {
if widgetGroup.GetVisible() {
widgetGroup.Render(target)
}
}
for _, widget := range ui.widgets {
if widget.GetVisible() {
widget.Render(target)
}
}
}
// Advance updates all of the UI elements

View File

@ -11,6 +11,8 @@ const (
RenderPrioritySkilltree
// RenderPrioritySkilltreeIcon is the priority for the skilltree icons
RenderPrioritySkilltreeIcon
// RenderPriorityMinipanel is the priority for the minipanel icons
RenderPriorityMinipanel
// RenderPriorityHeroStatsPanel is the priority for the hero_stats_panel
RenderPriorityHeroStatsPanel
// RenderPriorityForeground is the last element drawn
@ -22,6 +24,7 @@ type Widget interface {
Drawable
bindManager(ui *UIManager)
GetManager() (ui *UIManager)
OnMouseMove(x int, y int)
OnHoverStart(callback func())
OnHoverEnd(callback func())
isHovered() bool
@ -156,3 +159,14 @@ func (b *BaseWidget) Contains(x, y int) bool {
func (b *BaseWidget) GetManager() (ui *UIManager) {
return b.manager
}
// OnMouseMove is called when the mouse is moved
func (b *BaseWidget) OnMouseMove(x, y int) {
if b.Contains(x, y) {
if !b.isHovered() {
b.hoverStart()
}
} else if b.isHovered() {
b.hoverEnd()
}
}

View File

@ -1,11 +1,14 @@
package d2ui
import (
"image/color"
"sort"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
)
const widgetGroupDebug = false // turns on debug rendering stuff for groups
// static check that WidgetGroup implements widget
var _ Widget = &WidgetGroup{}
@ -44,8 +47,8 @@ func (wg *WidgetGroup) adjustSize(w Widget) {
x, y := w.GetPosition()
width, height := w.GetSize()
if x+width > wg.width {
wg.width = x + width
if x+width > wg.x+wg.width {
wg.width += (x + width) - (wg.x + wg.width)
}
if wg.x > x {
@ -53,8 +56,8 @@ func (wg *WidgetGroup) adjustSize(w Widget) {
wg.x = x
}
if y+height > wg.height {
wg.height = x + height
if y+height > wg.y+wg.height {
wg.height += (y + height) - (wg.y + wg.height)
}
if wg.y > y {
@ -76,15 +79,42 @@ func (wg *WidgetGroup) Render(target d2interface.Surface) {
entry.Render(target)
}
}
if widgetGroupDebug && wg.GetVisible() {
wg.renderDebug(target)
}
}
func (wg *WidgetGroup) renderDebug(target d2interface.Surface) {
target.PushTranslation(wg.GetPosition())
defer target.Pop()
target.DrawLine(wg.width, 0, color.White)
target.DrawLine(0, wg.height, color.White)
target.PushTranslation(wg.width, wg.height)
target.DrawLine(-wg.width, 0, color.White)
target.DrawLine(0, -wg.height, color.White)
target.Pop()
}
// SetVisible sets the visibility of all widgets in the group
func (wg *WidgetGroup) SetVisible(visible bool) {
wg.BaseWidget.SetVisible(visible)
for _, entry := range wg.entries {
entry.SetVisible(visible)
}
}
// OffsetPosition moves all widgets by x and y
func (wg *WidgetGroup) OffsetPosition(x, y int) {
wg.BaseWidget.OffsetPosition(x, y)
for _, entry := range wg.entries {
entry.OffsetPosition(x, y)
}
}
// OnMouseMove handles mouse move events
func (wg *WidgetGroup) OnMouseMove(x, y int) {
for _, entry := range wg.entries {

View File

@ -80,6 +80,8 @@ type EscapeMenu struct {
assetManager *d2asset.AssetManager
keyMap *KeyMap
keyBindingMenu *KeyBindingMenu
onCloseCb func()
}
type layout struct {
@ -410,10 +412,16 @@ func (m *EscapeMenu) OnEscKey() {
m.close()
}
// SetOnCloseCb sets the callback that is run when close() is called
func (m *EscapeMenu) SetOnCloseCb(cb func()) {
m.onCloseCb = cb
}
func (m *EscapeMenu) close() {
m.isOpen = false
m.guiManager.SetLayout(nil)
m.onCloseCb()
}
func (m *EscapeMenu) open() {

View File

@ -39,18 +39,10 @@ const (
xp
walkRun
stamina
miniPnl
newSkills
rightSkill
hpGlobe
manaGlobe
miniPanelCharacter
miniPanelInventory
miniPanelSkillTree
miniPanelAutomap
miniPanelMessageLog
miniPanelQuestLog
miniPanelGameMenu
)
const (
@ -79,11 +71,6 @@ const (
staminaWidth,
staminaHeight = 273, 573, 105, 20
miniPnlX,
miniPnlY,
miniPnlWidth,
miniPnlHeight = 393, 563, 12, 23
newSkillsX,
newSkillsY,
newSkillsWidth,
@ -103,41 +90,6 @@ const (
manaGlobeY,
manaGlobeWidth,
manaGlobeHeight = 695, 525, 80, 60
miniPanelCharacterX,
miniPanelCharacterY,
miniPanelCharacterWidth,
miniPanelCharacterHeight = 324, 528, 22, 26
miniPanelInventoryX,
miniPanelInventoryY,
miniPanelInventoryWidth,
miniPanelInventoryHeight = 346, 528, 22, 26
miniPanelSkillTreeX,
miniPanelSkillTreeY,
miniPanelSkillTreeWidth,
miniPanelSkillTreeHeight = 368, 528, 22, 26
miniPanelAutomapX,
miniPanelAutomapY,
miniPanelAutomapWidth,
miniPanelAutomapHeight = 390, 528, 22, 26
miniPanelMessageLogX,
miniPanelMessageLogY,
miniPanelMessageLogWidth,
miniPanelMessageLogHeight = 412, 528, 22, 26
miniPanelQuestLogX,
miniPanelQuestLogY,
miniPanelQuestLogWidth,
miniPanelQuestLogHeight = 434, 528, 22, 26
miniPanelGameMenuX,
miniPanelGameMenuY,
miniPanelGameMenuWidth,
miniPanelGameMenuHeight = 456, 528, 22, 26
)
const (
@ -269,12 +221,6 @@ func NewGameControls(
Width: staminaWidth,
Height: staminaHeight,
}},
{miniPnl, d2geom.Rectangle{
Left: miniPnlX,
Top: miniPnlY,
Width: miniPnlWidth,
Height: miniPnlHeight,
}},
{newSkills, d2geom.Rectangle{
Left: newSkillsX,
Top: newSkillsY,
@ -299,59 +245,22 @@ func NewGameControls(
Width: manaGlobeWidth,
Height: manaGlobeHeight,
}},
{miniPanelCharacter, d2geom.Rectangle{
Left: miniPanelCharacterX,
Top: miniPanelCharacterY,
Width: miniPanelCharacterWidth,
Height: miniPanelCharacterHeight,
}},
{miniPanelInventory, d2geom.Rectangle{
Left: miniPanelInventoryX,
Top: miniPanelInventoryY,
Width: miniPanelInventoryWidth,
Height: miniPanelInventoryHeight,
}},
{miniPanelSkillTree, d2geom.Rectangle{
Left: miniPanelSkillTreeX,
Top: miniPanelSkillTreeY,
Width: miniPanelSkillTreeWidth,
Height: miniPanelSkillTreeHeight,
}},
{miniPanelAutomap, d2geom.Rectangle{
Left: miniPanelAutomapX,
Top: miniPanelAutomapY,
Width: miniPanelAutomapWidth,
Height: miniPanelAutomapHeight,
}},
{miniPanelMessageLog, d2geom.Rectangle{
Left: miniPanelMessageLogX,
Top: miniPanelMessageLogY,
Width: miniPanelMessageLogWidth,
Height: miniPanelMessageLogHeight,
}},
{miniPanelQuestLog, d2geom.Rectangle{
Left: miniPanelQuestLogX,
Top: miniPanelQuestLogY,
Width: miniPanelQuestLogWidth,
Height: miniPanelQuestLogHeight,
}},
{miniPanelGameMenu, d2geom.Rectangle{
Left: miniPanelGameMenuX,
Top: miniPanelGameMenuY,
Width: miniPanelGameMenuWidth,
Height: miniPanelGameMenuHeight,
}},
}
inventoryRecord := asset.Records.Layout.Inventory[inventoryRecordKey]
heroStatsPanel := NewHeroStatsPanel(asset, ui, hero.Name(), hero.Class, hero.Stats)
inventory := NewInventory(asset, ui, inventoryRecord)
skilltree := newSkillTree(hero.Skills, hero.Class, asset, ui)
miniPanel := newMiniPanel(asset, ui, isSinglePlayer)
heroState, err := d2hero.NewHeroStateFactory(asset)
if err != nil {
return nil, err
}
helpOverlay := NewHelpOverlay(asset, renderer, ui, guiManager, keyMap)
hud := NewHUD(asset, ui, hero, helpOverlay, newMiniPanel(asset, ui, isSinglePlayer), actionableRegions, mapEngine, mapRenderer)
hud := NewHUD(asset, ui, hero, helpOverlay, miniPanel, actionableRegions, mapEngine, mapRenderer)
const blackAlpha50percent = 0x0000007f
@ -367,9 +276,9 @@ func NewGameControls(
escapeMenu: escapeMenu,
inputListener: inputListener,
mapRenderer: mapRenderer,
inventory: NewInventory(asset, ui, inventoryRecord),
skilltree: newSkillTree(hero.Skills, hero.Class, asset, ui),
heroStatsPanel: NewHeroStatsPanel(asset, ui, hero.Name(), hero.Class, hero.Stats),
inventory: inventory,
skilltree: skilltree,
heroStatsPanel: heroStatsPanel,
HelpOverlay: helpOverlay,
keyMap: keyMap,
hud: hud,
@ -397,10 +306,11 @@ func NewGameControls(
isSinglePlayer: isSinglePlayer,
}
closeCb := func() { gc.updateLayout() }
gc.heroStatsPanel.SetOnCloseCb(closeCb)
gc.inventory.SetOnCloseCb(closeCb)
gc.skilltree.SetOnCloseCb(closeCb)
gc.heroStatsPanel.SetOnCloseCb(gc.onCloseHeroStatsPanel)
gc.inventory.SetOnCloseCb(gc.onCloseInventory)
gc.skilltree.SetOnCloseCb(gc.onCloseSkilltree)
gc.escapeMenu.SetOnCloseCb(gc.hud.restoreMinipanelFromTempClose)
err = gc.bindTerminalCommands(term)
if err != nil {
@ -467,14 +377,11 @@ func (g *GameControls) OnKeyDown(event d2interface.KeyEvent) bool {
g.HelpOverlay.Close()
g.updateLayout()
case d2enum.ToggleInventoryPanel:
g.inventory.Toggle()
g.updateLayout()
g.toggleInventoryPanel()
case d2enum.ToggleSkillTreePanel:
g.skilltree.Toggle()
g.updateLayout()
g.toggleInventoryPanel()
case d2enum.ToggleCharacterPanel:
g.heroStatsPanel.Toggle()
g.updateLayout()
g.toggleHeroStatsPanel()
case d2enum.ToggleRunWalk:
g.hud.onToggleRunButton(false)
case d2enum.HoldRun:
@ -547,7 +454,7 @@ func (g *GameControls) onEscKey() {
if g.escapeMenu.IsOpen() {
g.escapeMenu.OnEscKey()
} else {
g.escapeMenu.open()
g.openEscMenu()
}
}
}
@ -690,6 +597,50 @@ func (g *GameControls) OnMouseButtonDown(event d2interface.MouseEvent) bool {
return false
}
func (g *GameControls) toggleHeroStatsPanel() {
g.heroStatsPanel.Toggle()
g.hud.miniPanel.SetMovedRight(g.heroStatsPanel.IsOpen())
g.updateLayout()
}
func (g *GameControls) onCloseHeroStatsPanel() {
g.hud.miniPanel.SetMovedRight(g.heroStatsPanel.IsOpen())
g.updateLayout()
}
func (g *GameControls) toggleInventoryPanel() {
g.skilltree.Close()
g.inventory.Toggle()
g.hud.miniPanel.SetMovedLeft(g.inventory.IsOpen())
g.updateLayout()
}
func (g *GameControls) onCloseInventory() {
g.hud.miniPanel.SetMovedLeft(g.inventory.IsOpen())
g.updateLayout()
}
func (g *GameControls) toggleSkilltreePanel() {
g.inventory.Close()
g.skilltree.Toggle()
g.hud.miniPanel.SetMovedLeft(g.skilltree.IsOpen())
g.updateLayout()
}
func (g *GameControls) onCloseSkilltree() {
g.hud.miniPanel.SetMovedLeft(g.skilltree.IsOpen())
g.updateLayout()
}
func (g *GameControls) openEscMenu() {
g.inventory.Close()
g.skilltree.Close()
g.heroStatsPanel.Close()
g.hud.closeMinipanelTemporary()
g.escapeMenu.open()
g.updateLayout()
}
// Load the resources required for the GameControls
func (g *GameControls) Load() {
g.hud.Load()
@ -697,6 +648,14 @@ func (g *GameControls) Load() {
g.skilltree.load()
g.heroStatsPanel.Load()
g.HelpOverlay.Load()
miniPanelActions := &miniPanelActions{
characterToggle: g.toggleHeroStatsPanel,
inventoryToggle: g.toggleInventoryPanel,
skilltreeToggle: g.toggleSkilltreePanel,
menuToggle: g.openEscMenu,
}
g.hud.miniPanel.load(miniPanelActions)
}
// Advance advances the state of the GameControls
@ -746,7 +705,7 @@ func (g *GameControls) isInActiveMenusRect(px, py int) bool {
return true
}
if g.hud.miniPanel.IsOpen() && g.hud.miniPanel.isInRect(px, py) {
if g.hud.miniPanel.IsOpen() && g.hud.miniPanel.IsInRect(px, py) {
return true
}
@ -828,23 +787,15 @@ func (g *GameControls) ToggleManaStats() {
// Handles what to do when an actionable is hovered
func (g *GameControls) onHoverActionable(item actionableType) {
hoverMap := map[actionableType]func(){
leftSkill: func() {},
newStats: func() {},
xp: func() {},
walkRun: func() {},
stamina: func() {},
miniPnl: func() {},
newSkills: func() {},
rightSkill: func() {},
hpGlobe: func() {},
manaGlobe: func() {},
miniPanelCharacter: func() {},
miniPanelInventory: func() {},
miniPanelSkillTree: func() {},
miniPanelAutomap: func() {},
miniPanelMessageLog: func() {},
miniPanelQuestLog: func() {},
miniPanelGameMenu: func() {},
leftSkill: func() {},
newStats: func() {},
xp: func() {},
walkRun: func() {},
stamina: func() {},
newSkills: func() {},
rightSkill: func() {},
hpGlobe: func() {},
manaGlobe: func() {},
}
onHover, found := hoverMap[item]
@ -879,12 +830,6 @@ func (g *GameControls) onClickActionable(item actionableType) {
log.Println("Stamina Action Pressed")
},
miniPnl: func() {
log.Println("Mini Panel Action Pressed")
g.hud.miniPanel.Toggle()
},
newSkills: func() {
log.Println("New Skills Selector Action Pressed")
},
@ -902,32 +847,6 @@ func (g *GameControls) onClickActionable(item actionableType) {
g.ToggleManaStats()
log.Println("Mana Globe Pressed")
},
miniPanelCharacter: func() {
log.Println("Character button on mini panel is pressed")
g.heroStatsPanel.Toggle()
g.updateLayout()
},
miniPanelInventory: func() {
log.Println("Inventory button on mini panel is pressed")
g.inventory.Toggle()
g.updateLayout()
},
miniPanelSkillTree: func() {
log.Println("Skilltree button on mini panel is pressed")
g.skilltree.Toggle()
g.updateLayout()
},
miniPanelGameMenu: func() {
g.hud.miniPanel.Close()
g.escapeMenu.open()
},
}
action, found := actionMap[item]

View File

@ -131,9 +131,7 @@ func (s *HeroStatsPanel) Load() {
log.Print(err)
}
fw, fh := frame.GetFrameBounds()
fc := frame.GetFrameCount()
w, h := fw*fc, fh*fc
w, h := frame.GetSize()
staticPanel := s.uiManager.NewCustomWidgetCached(s.renderStaticMenu, w, h)
s.panelGroup.AddWidget(staticPanel)

View File

@ -47,7 +47,6 @@ const (
)
const (
frameMenuButton = 2
frameHealthStatus = 0
frameManaStatus = 1
frameNewStatsSelector = 1
@ -69,7 +68,7 @@ const (
rightGlobeOffsetY = -8
miniPanelButtonOffsetX = -8
miniPanelButtonOffsetY = -16
miniPanelButtonOffsetY = -38
)
const (
@ -91,13 +90,14 @@ type HUD struct {
hero *d2mapentity.Player
mainPanel *d2ui.Sprite
globeSprite *d2ui.Sprite
menuButton *d2ui.Sprite
menuButton *d2ui.Button
hpManaStatusSprite *d2ui.Sprite
leftSkillResource *SkillResource
rightSkillResource *SkillResource
runButton *d2ui.Button
zoneChangeText *d2ui.Label
miniPanel *miniPanel
isMiniPanelOpen bool
isZoneTextShown bool
hpStatsIsVisible bool
manaStatsIsVisible bool
@ -241,16 +241,6 @@ func (h *HUD) loadSprites() {
log.Print(err)
}
h.menuButton, err = h.uiManager.NewSprite(d2resource.MenuButton, d2resource.PaletteSky)
if err != nil {
log.Print(err)
}
err = h.menuButton.SetCurrentFrame(frameMenuButton)
if err != nil {
log.Print(err)
}
h.mainPanel, err = h.uiManager.NewSprite(d2resource.GamePanels, d2resource.PaletteSky)
if err != nil {
log.Print(err)
@ -325,6 +315,17 @@ func (h *HUD) loadUIButtons() {
if h.hero.IsRunToggled() {
h.runButton.Toggle()
}
// minipanel button
h.menuButton = h.uiManager.NewButton(d2ui.ButtonTypeMinipanelOpenClose, "")
//nolint:golint,gomnd // 2 is not a magic number
x := screenWidth/2 + miniPanelButtonOffsetX
y := screenHeight + miniPanelButtonOffsetY
h.menuButton.SetPosition(x, y)
h.menuButton.OnActivated(func() {
h.menuButton.Toggle()
h.miniPanel.Toggle()
})
}
func (h *HUD) onToggleRunButton(noButton bool) {
@ -493,65 +494,6 @@ func (h *HUD) renderExperienceBar(target d2interface.Surface) {
target.DrawRect(int(expPercent*expBarWidth), 2, d2util.Color(whiteAlpha100))
}
func (h *HUD) renderMiniPanel(target d2interface.Surface) error {
width, height := target.GetSize()
mx, my := h.lastMouseX, h.lastMouseY
menuButtonFrameIndex := 0
if h.miniPanel.isOpen {
menuButtonFrameIndex = 2
}
if err := h.menuButton.SetCurrentFrame(menuButtonFrameIndex); err != nil {
return err
}
buttonX, buttonY := (width>>1)+miniPanelButtonOffsetX, height+miniPanelButtonOffsetY
h.menuButton.SetPosition(buttonX, buttonY)
h.menuButton.Render(target)
h.miniPanel.Render(target)
miniPanelButtons := map[actionableType]string{
miniPanelCharacter: "minipanelchar",
miniPanelInventory: "minipanelinv",
miniPanelSkillTree: "minipaneltree",
miniPanelAutomap: "minipanelautomap",
miniPanelMessageLog: "minipanelmessage",
miniPanelQuestLog: "minipanelquest",
miniPanelGameMenu: "minipanelmenubtn",
}
if !h.miniPanel.IsOpen() {
return nil
}
for miniPanelButton, stringTableKey := range miniPanelButtons {
if !h.actionableRegions[miniPanelButton].rect.IsInRect(mx, my) {
continue
}
rect := &h.actionableRegions[miniPanelButton].rect
h.miniPanelTooltip.SetText(h.asset.TranslateString(stringTableKey))
halfButtonWidth := rect.Width >> 1
halfButtonHeight := rect.Height >> 1
centerX := rect.Left + halfButtonWidth
centerY := rect.Top + halfButtonHeight
_, labelHeight := h.miniPanelTooltip.GetSize()
labelX := centerX
labelY := centerY - halfButtonHeight - labelHeight
h.miniPanelTooltip.SetPosition(labelX, labelY)
h.miniPanelTooltip.Render(target)
}
return nil
}
func (h *HUD) renderPotions(x, _ int, target d2interface.Surface) error {
_, height := target.GetSize()
@ -726,11 +668,6 @@ func (h *HUD) Render(target d2interface.Surface) error {
h.widgetStamina.Render(target)
h.widgetExperience.Render(target)
// Mini Panel and button
if err := h.renderMiniPanel(target); err != nil {
return err
}
if err := h.help.Render(target); err != nil {
return err
}
@ -784,3 +721,20 @@ func (h *HUD) OnMouseMove(event d2interface.MouseMoveEvent) bool {
return false
}
func (h *HUD) closeMinipanelTemporary() {
h.isMiniPanelOpen = h.miniPanel.IsOpen()
if h.isMiniPanelOpen {
h.menuButton.SetEnabled(false)
h.menuButton.Toggle()
h.miniPanel.Close()
}
}
func (h *HUD) restoreMinipanelFromTempClose() {
if h.isMiniPanelOpen {
h.menuButton.SetEnabled(true)
h.menuButton.Toggle()
h.miniPanel.Open()
}
}

View File

@ -3,74 +3,192 @@ package d2player
import (
"log"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2geom"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui"
)
const (
miniPanelX = 325
miniPanelY = 526
miniPanelWidth = 156
miniPanelHeight = 26
miniPanelX = 325
miniPanelY = 526
panelOffsetLeft = 130
panelOffsetRight = 130
)
const (
containerOffsetX = -75
containerOffsetY = -48
containerOffsetY = -49
buttonOffsetX = -72
buttonOffsetY = -51
buttonOffsetY = -52
)
type miniPanelContent struct {
buttonType d2ui.ButtonType
onActivate func()
tooltip string
}
type miniPanelActions struct {
characterToggle func()
inventoryToggle func()
skilltreeToggle func()
partyToggle func()
automapToggle func()
messageToggle func()
questToggle func()
menuToggle func()
}
type miniPanel struct {
ui *d2ui.UIManager
asset *d2asset.AssetManager
container *d2ui.Sprite
button *d2ui.Sprite
sprite *d2ui.Sprite
isOpen bool
isSinglePlayer bool
rectangle d2geom.Rectangle
movedLeft bool
movedRight bool
panelGroup *d2ui.WidgetGroup
tooltipGroup *d2ui.WidgetGroup
}
func newMiniPanel(asset *d2asset.AssetManager, uiManager *d2ui.UIManager, isSinglePlayer bool) *miniPanel {
return &miniPanel{
ui: uiManager,
asset: asset,
isOpen: false,
isSinglePlayer: isSinglePlayer,
}
}
func (m *miniPanel) load(actions *miniPanelActions) {
var err error
m.sprite, err = m.ui.NewSprite(d2resource.MinipanelButton, d2resource.PaletteSky)
if err != nil {
log.Print(err)
return
}
m.createWidgets(actions)
}
func (m *miniPanel) createWidgets(actions *miniPanelActions) {
var err error
m.panelGroup = m.ui.NewWidgetGroup(d2ui.RenderPriorityMinipanel)
m.panelGroup.SetPosition(miniPanelX, miniPanelY)
m.tooltipGroup = m.ui.NewWidgetGroup(d2ui.RenderPriorityForeground)
miniPanelContainerPath := d2resource.Minipanel
if isSinglePlayer {
if m.isSinglePlayer {
miniPanelContainerPath = d2resource.MinipanelSmall
}
containerSprite, err := uiManager.NewSprite(miniPanelContainerPath, d2resource.PaletteSky)
m.container, err = m.ui.NewSprite(miniPanelContainerPath, d2resource.PaletteSky)
if err != nil {
log.Print(err)
return nil
return
}
buttonSprite, err := uiManager.NewSprite(d2resource.MinipanelButton, d2resource.PaletteSky)
if err = m.container.SetCurrentFrame(0); err != nil {
log.Print(err)
return
}
// nolint:golint,gomnd // divide by 2 does not need a magic number
x, y := screenWidth/2+containerOffsetX, screenHeight+containerOffsetY
m.container.SetPosition(x, y)
m.panelGroup.AddWidget(m.container)
buttonWidth, buttonHeight, err := m.sprite.GetFrameSize(0)
if err != nil {
log.Print(err)
return nil
return
}
rectangle := d2geom.Rectangle{
Left: miniPanelX,
Top: miniPanelY,
Width: miniPanelWidth,
Height: miniPanelHeight,
buttonWidth++
// nolint:golint,gomnd // divide by 2 does not need a magic number
x, y = screenWidth/2+buttonOffsetX, screenHeight+buttonOffsetY-buttonHeight
buttonsFirst := []miniPanelContent{
{d2ui.ButtonTypeMinipanelCharacter,
actions.characterToggle,
m.asset.TranslateString("minipanelchar"),
},
{d2ui.ButtonTypeMinipanelInventory,
actions.inventoryToggle,
m.asset.TranslateString("minipanelinv"),
},
{d2ui.ButtonTypeMinipanelSkill,
actions.skilltreeToggle,
m.asset.TranslateString("minipaneltree"),
},
}
if !isSinglePlayer {
rectangle.Width = 182
for i := range buttonsFirst {
btn := m.createButton(buttonsFirst[i], x+(i*buttonWidth), y, buttonHeight)
m.panelGroup.AddWidget(btn)
}
return &miniPanel{
asset: asset,
container: containerSprite,
button: buttonSprite,
isOpen: false,
isSinglePlayer: isSinglePlayer,
rectangle: rectangle,
idxOffset := len(buttonsFirst)
if !m.isSinglePlayer {
partyContent := miniPanelContent{d2ui.ButtonTypeMinipanelParty,
actions.partyToggle,
m.asset.TranslateString("minipanelparty"),
}
btn := m.createButton(partyContent, x+(3*buttonWidth), y, buttonHeight)
m.panelGroup.AddWidget(btn)
idxOffset++
}
buttonsLast := []miniPanelContent{
{d2ui.ButtonTypeMinipanelAutomap,
actions.automapToggle,
m.asset.TranslateString("minipanelautomap"),
},
{d2ui.ButtonTypeMinipanelMessage,
actions.messageToggle,
m.asset.TranslateString("minipanelmessage"),
},
{d2ui.ButtonTypeMinipanelQuest,
actions.questToggle,
m.asset.TranslateString("minipanelquest"),
},
{d2ui.ButtonTypeMinipanelMen,
actions.menuToggle,
m.asset.TranslateString("minipanelmenubtn"),
},
}
for i := range buttonsLast {
idx := i + idxOffset
btn := m.createButton(buttonsLast[i], x+(idx*buttonWidth), y, buttonHeight)
m.panelGroup.AddWidget(btn)
}
m.panelGroup.SetVisible(false)
}
func (m *miniPanel) createButton(content miniPanelContent, x, y, buttonHeight int) *d2ui.Button {
// Tooltip
tt := m.ui.NewTooltip(d2resource.Font16, d2resource.PaletteSky, d2ui.TooltipXCenter, d2ui.TooltipYTop)
tt.SetPosition(x, y-buttonHeight)
tt.SetText(content.tooltip)
tt.SetVisible(false)
m.tooltipGroup.AddWidget(tt)
// Button
btn := m.ui.NewButton(content.buttonType, "")
btn.SetPosition(x, y)
btn.OnActivated(content.onActivate)
btn.SetTooltip(tt)
return btn
}
func (m *miniPanel) IsOpen() bool {
@ -78,56 +196,91 @@ func (m *miniPanel) IsOpen() bool {
}
func (m *miniPanel) Toggle() {
m.isOpen = !m.isOpen
if m.isOpen {
m.Close()
} else {
m.Open()
}
}
func (m *miniPanel) Open() {
m.panelGroup.SetVisible(true)
m.isOpen = true
}
func (m *miniPanel) Close() {
m.panelGroup.SetVisible(false)
m.isOpen = false
}
func (m *miniPanel) Render(target d2interface.Surface) {
if !m.isOpen {
func (m *miniPanel) IsInRect(px, py int) bool {
return m.panelGroup.Contains(px, py)
}
func (m *miniPanel) moveRight() {
m.panelGroup.OffsetPosition(panelOffsetRight, 0)
m.tooltipGroup.OffsetPosition(panelOffsetRight, 0)
}
func (m *miniPanel) undoMoveRight() {
m.panelGroup.OffsetPosition(-panelOffsetRight, 0)
m.tooltipGroup.OffsetPosition(-panelOffsetRight, 0)
}
func (m *miniPanel) moveLeft() {
m.panelGroup.OffsetPosition(-panelOffsetLeft, 0)
m.tooltipGroup.OffsetPosition(-panelOffsetLeft, 0)
}
func (m *miniPanel) undoMoveLeft() {
m.panelGroup.OffsetPosition(panelOffsetLeft, 0)
m.tooltipGroup.OffsetPosition(panelOffsetLeft, 0)
}
func (m *miniPanel) SetMovedLeft(moveLeft bool) {
if m.movedLeft == moveLeft {
return
}
if err := m.container.SetCurrentFrame(0); err != nil {
if m.movedRight {
if moveLeft {
m.undoMoveRight()
m.panelGroup.SetVisible(false)
} else {
m.moveRight()
m.panelGroup.SetVisible(true)
}
} else {
if moveLeft {
m.moveLeft()
} else {
m.undoMoveLeft()
}
}
m.movedLeft = moveLeft
}
func (m *miniPanel) SetMovedRight(moveRight bool) {
if m.movedRight == moveRight {
return
}
width, height := target.GetSize()
halfW := width >> 1
x, y := halfW+containerOffsetX, height+containerOffsetY
m.container.SetPosition(x, y)
m.container.Render(target)
buttonWidth, _ := m.button.GetCurrentFrameSize()
buttonWidth++
for i, j := 0, 0; j < 16; i++ {
if m.isSinglePlayer && j == 6 { // skip Party Screen button if the game is single player
j += 2
if m.movedLeft {
if moveRight {
m.undoMoveLeft()
m.panelGroup.SetVisible(false)
} else {
m.moveLeft()
m.panelGroup.SetVisible(true)
}
if err := m.button.SetCurrentFrame(j); err != nil {
return
} else {
if moveRight {
m.moveRight()
} else {
m.undoMoveRight()
}
offsetX := buttonOffsetX + (buttonWidth * i)
x, y := halfW+offsetX, height+buttonOffsetY
m.button.SetPosition(x, y)
m.button.Render(target)
j += 2
}
}
func (m *miniPanel) isInRect(x, y int) bool {
return m.rectangle.IsInRect(x, y)
m.movedRight = moveRight
}

View File

@ -357,6 +357,7 @@ func (s *skillTree) Open() {
s.isOpen = true
s.panelGroup.SetVisible(true)
s.iconGroup.SetVisible(true)
// we only want to enable the icons of our current tab again
s.setTab(s.selectedTab)