mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2024-11-12 15:27:31 -05:00
Tooltip refactor (#872)
* d2ui: Add tooltip class we reimplemented tooltips in several places (inventory/skill_select_panel). Let's make it its own ui element. * d2player: Refactor skill_select_panel to use the tooltip class * d2player: Refactor inventory to use the tooltip class * Make linter happy * Make golangci-lint 1.27.0 happy as well * tooltip: Remove const and rather disable linter
This commit is contained in:
parent
4a62101b96
commit
79c147866e
188
d2core/d2ui/tooltip.go
Normal file
188
d2core/d2ui/tooltip.go
Normal file
@ -0,0 +1,188 @@
|
||||
package d2ui
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui"
|
||||
)
|
||||
|
||||
const (
|
||||
blackAlpha70 = 0x000000C8
|
||||
screenWidth = 800
|
||||
screenHeight = 600
|
||||
)
|
||||
|
||||
// Tooltip contains a label containing text with a transparent, black background
|
||||
type Tooltip struct {
|
||||
manager *UIManager
|
||||
lines []string
|
||||
label *Label
|
||||
backgroundColor int
|
||||
x, y int
|
||||
originX tooltipXOrigin
|
||||
originY tooltipYOrigin
|
||||
}
|
||||
|
||||
type tooltipXOrigin = int
|
||||
type tooltipYOrigin = int
|
||||
|
||||
const (
|
||||
// TooltipYTop sets the Y origin of the tooltip to the top
|
||||
TooltipYTop tooltipYOrigin = iota
|
||||
// TooltipYCenter sets the Y origin of the tooltip to the center
|
||||
TooltipYCenter
|
||||
// TooltipYBottom sets the Y origin of the tooltip to the bottom
|
||||
TooltipYBottom
|
||||
)
|
||||
|
||||
const (
|
||||
// TooltipXLeft sets the X origin of the tooltip to the left
|
||||
TooltipXLeft tooltipXOrigin = iota
|
||||
// TooltipXCenter sets the X origin of the tooltip to the center
|
||||
TooltipXCenter
|
||||
// TooltipXRight sets the X origin of the tooltip to the right
|
||||
TooltipXRight
|
||||
)
|
||||
|
||||
// NewTooltip creates a tooltip instance. Note here, that we need to define the
|
||||
// orign point of the tooltip rect using tooltipXOrigin and tooltinYOrigin
|
||||
func (ui *UIManager) NewTooltip(font,
|
||||
palette string,
|
||||
originX tooltipXOrigin,
|
||||
originY tooltipYOrigin) *Tooltip {
|
||||
label := ui.NewLabel(font, palette)
|
||||
label.Alignment = d2gui.HorizontalAlignCenter
|
||||
|
||||
res := &Tooltip{
|
||||
backgroundColor: blackAlpha70,
|
||||
label: label,
|
||||
x: 0,
|
||||
y: 0,
|
||||
originX: originX,
|
||||
originY: originY,
|
||||
}
|
||||
res.manager = ui
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// SetPosition sets the position of the origin point of the tooltip
|
||||
func (t *Tooltip) SetPosition(x, y int) {
|
||||
t.x = x
|
||||
t.y = y
|
||||
}
|
||||
|
||||
// SetTextLines sets the tooltip text in the form of an array of strings
|
||||
func (t *Tooltip) SetTextLines(lines []string) {
|
||||
t.lines = lines
|
||||
}
|
||||
|
||||
// SetText sets the tooltip text and splits \n into lines
|
||||
func (t *Tooltip) SetText(text string) {
|
||||
t.lines = strings.Split(text, "\n")
|
||||
}
|
||||
|
||||
func (t *Tooltip) adjustCoordinatesToScreen(maxW, maxH, halfW, halfH int) (rx, ry int) {
|
||||
var xOffset, yOffset int
|
||||
|
||||
switch t.originX {
|
||||
case TooltipXLeft:
|
||||
xOffset = maxW
|
||||
case TooltipXCenter:
|
||||
xOffset = halfW
|
||||
case TooltipXRight:
|
||||
xOffset = 0
|
||||
}
|
||||
|
||||
renderX := t.x
|
||||
if (t.x + xOffset) > screenWidth {
|
||||
renderX = screenWidth - xOffset
|
||||
}
|
||||
|
||||
switch t.originY {
|
||||
case TooltipYTop:
|
||||
yOffset = 0
|
||||
case TooltipYCenter:
|
||||
yOffset = halfH
|
||||
case TooltipYBottom:
|
||||
yOffset = maxH
|
||||
}
|
||||
|
||||
renderY := t.y
|
||||
if (t.y + yOffset) > screenHeight {
|
||||
renderY = screenHeight - yOffset
|
||||
}
|
||||
|
||||
return renderX, renderY
|
||||
}
|
||||
|
||||
func (t *Tooltip) getTextSize() (sx, sy int) {
|
||||
maxW, maxH := 0, 0
|
||||
|
||||
for i := range t.lines {
|
||||
w, h := t.label.GetTextMetrics(t.lines[i])
|
||||
|
||||
if maxW < w {
|
||||
maxW = w
|
||||
}
|
||||
|
||||
maxH += h
|
||||
}
|
||||
|
||||
return maxW, maxH
|
||||
}
|
||||
|
||||
// Render draws the tooltip
|
||||
func (t *Tooltip) Render(target d2interface.Surface) {
|
||||
maxW, maxH := t.getTextSize()
|
||||
|
||||
// nolint:gomnd // no magic numbers, their meaning is obvious
|
||||
halfW, halfH := maxW/2, maxH/2
|
||||
|
||||
renderX, renderY := t.adjustCoordinatesToScreen(maxW, maxH, halfW, halfH)
|
||||
|
||||
target.PushTranslation(renderX, renderY)
|
||||
defer target.Pop()
|
||||
|
||||
// adjust starting point of the background rect based on the origin point
|
||||
// as we always draw a rect from top left
|
||||
switch t.originX {
|
||||
case TooltipXLeft:
|
||||
target.PushTranslation(0, 0)
|
||||
case TooltipXCenter:
|
||||
target.PushTranslation(-halfW, 0)
|
||||
case TooltipXRight:
|
||||
target.PushTranslation(-maxW, 0)
|
||||
}
|
||||
|
||||
defer target.Pop()
|
||||
|
||||
switch t.originY {
|
||||
case TooltipYTop:
|
||||
target.PushTranslation(0, 0)
|
||||
case TooltipYCenter:
|
||||
target.PushTranslation(0, -halfH)
|
||||
case TooltipXRight:
|
||||
target.PushTranslation(0, -maxH)
|
||||
}
|
||||
|
||||
defer target.Pop()
|
||||
|
||||
// tooltip background
|
||||
target.DrawRect(maxW, maxH, d2util.Color(blackAlpha70))
|
||||
|
||||
// text
|
||||
target.PushTranslation(halfW, 0) // text is centered, our box is not
|
||||
defer target.Pop()
|
||||
|
||||
for i := range t.lines {
|
||||
t.label.SetText(t.lines[i])
|
||||
_, h := t.label.GetTextMetrics(t.lines[i])
|
||||
t.label.Render(target)
|
||||
target.PushTranslation(0, h)
|
||||
}
|
||||
|
||||
target.PopN(len(t.lines))
|
||||
}
|
@ -3,14 +3,12 @@ package d2player
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2records"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2item/diablo2item"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui"
|
||||
)
|
||||
@ -22,10 +20,6 @@ const (
|
||||
frameInventoryBottomRight = 7
|
||||
)
|
||||
|
||||
const (
|
||||
blackAlpha70 = 0x000000C8
|
||||
)
|
||||
|
||||
const (
|
||||
invCloseButtonX, invCloseButtonY = 419, 449
|
||||
)
|
||||
@ -38,7 +32,7 @@ type Inventory struct {
|
||||
frame *d2ui.UIFrame
|
||||
panel *d2ui.Sprite
|
||||
grid *ItemGrid
|
||||
hoverLabel *d2ui.Label
|
||||
itemTooltip *d2ui.Tooltip
|
||||
closeButton *d2ui.Button
|
||||
hoverX int
|
||||
hoverY int
|
||||
@ -54,19 +48,18 @@ type Inventory struct {
|
||||
// NewInventory creates an inventory instance and returns a pointer to it
|
||||
func NewInventory(asset *d2asset.AssetManager, ui *d2ui.UIManager,
|
||||
record *d2records.InventoryRecord) *Inventory {
|
||||
hoverLabel := ui.NewLabel(d2resource.FontFormal11, d2resource.PaletteStatic)
|
||||
hoverLabel.Alignment = d2gui.HorizontalAlignCenter
|
||||
itemTooltip := ui.NewTooltip(d2resource.FontFormal11, d2resource.PaletteStatic, d2ui.TooltipXCenter, d2ui.TooltipYBottom)
|
||||
|
||||
// https://github.com/OpenDiablo2/OpenDiablo2/issues/797
|
||||
itemFactory, _ := diablo2item.NewItemFactory(asset)
|
||||
|
||||
return &Inventory{
|
||||
asset: asset,
|
||||
uiManager: ui,
|
||||
item: itemFactory,
|
||||
grid: NewItemGrid(asset, ui, record),
|
||||
originX: record.Panel.Left,
|
||||
hoverLabel: hoverLabel,
|
||||
asset: asset,
|
||||
uiManager: ui,
|
||||
item: itemFactory,
|
||||
grid: NewItemGrid(asset, ui, record),
|
||||
originX: record.Panel.Left,
|
||||
itemTooltip: itemTooltip,
|
||||
// originY: record.Panel.Top,
|
||||
originY: 0, // expansion data has these all offset by +60 ...
|
||||
}
|
||||
@ -250,48 +243,8 @@ func (g *Inventory) renderItemHover(target d2interface.Surface) {
|
||||
|
||||
func (g *Inventory) renderItemDescription(target d2interface.Surface, i InventoryItem) {
|
||||
lines := i.GetItemDescription()
|
||||
|
||||
maxW, maxH := 0, 0
|
||||
_, iy := g.grid.SlotToScreen(i.InventoryGridSlot())
|
||||
|
||||
for idx := range lines {
|
||||
w, h := g.hoverLabel.GetTextMetrics(lines[idx])
|
||||
|
||||
if maxW < w {
|
||||
maxW = w
|
||||
}
|
||||
|
||||
maxH += h
|
||||
}
|
||||
|
||||
halfW, halfH := maxW>>1, maxH>>1
|
||||
centerX, centerY := g.hoverX, iy-halfH
|
||||
|
||||
if (centerX + halfW) > screenWidth {
|
||||
centerX = screenWidth - halfW
|
||||
}
|
||||
|
||||
if (centerY + halfH) > screenHeight {
|
||||
centerY = screenHeight - halfH
|
||||
}
|
||||
|
||||
target.PushTranslation(centerX, centerY)
|
||||
defer target.Pop()
|
||||
|
||||
target.PushTranslation(-halfW, -halfH)
|
||||
defer target.Pop()
|
||||
|
||||
target.DrawRect(maxW, maxH, d2util.Color(blackAlpha70))
|
||||
|
||||
target.PushTranslation(halfW, 0)
|
||||
defer target.Pop()
|
||||
|
||||
for idx := range lines {
|
||||
g.hoverLabel.SetText(lines[idx])
|
||||
_, h := g.hoverLabel.GetTextMetrics(lines[idx])
|
||||
g.hoverLabel.Render(target)
|
||||
target.PushTranslation(0, h)
|
||||
}
|
||||
|
||||
target.PopN(len(lines))
|
||||
g.itemTooltip.SetTextLines(lines)
|
||||
_, y := g.grid.SlotToScreen(i.InventoryGridSlot())
|
||||
g.itemTooltip.SetPosition(g.hoverX, y)
|
||||
g.itemTooltip.Render(target)
|
||||
}
|
||||
|
@ -5,15 +5,12 @@ import (
|
||||
"log"
|
||||
"sort"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2tbl"
|
||||
"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/d2gui"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2hero"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapentity"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui"
|
||||
@ -28,9 +25,6 @@ const (
|
||||
leftPanelStartX = 90
|
||||
skillPanelOffsetY = 465
|
||||
skillListsLength = 5 // 0 to 4. 0 - General Skills, 1 to 3 - Class-specific skills(based on the 3 different skill trees), 4 - Other skills
|
||||
tooltipPadLeft = 3
|
||||
tooltipPadRight = 3
|
||||
tooltipPadBottom = 5
|
||||
)
|
||||
|
||||
// SkillPanel represents a skill select menu popup that is displayed when the player left clicks on his active left/right skill.
|
||||
@ -42,8 +36,7 @@ type SkillPanel struct {
|
||||
renderer d2interface.Renderer
|
||||
ui *d2ui.UIManager
|
||||
hoveredSkill *d2hero.HeroSkill
|
||||
hoverTooltipRect *d2geom.Rectangle
|
||||
hoverTooltipText *d2ui.Label
|
||||
hoverTooltip *d2ui.Tooltip
|
||||
isOpen bool
|
||||
regenerateImageCache bool
|
||||
isLeftPanel bool
|
||||
@ -58,19 +51,18 @@ func NewHeroSkillsPanel(asset *d2asset.AssetManager, ui *d2ui.UIManager, hero *d
|
||||
activeSkill = hero.RightSkill
|
||||
}
|
||||
|
||||
hoverTooltipText := ui.NewLabel(d2resource.Font16, d2resource.PaletteStatic)
|
||||
hoverTooltipText.Alignment = d2gui.HorizontalAlignCenter
|
||||
hoverTooltip := ui.NewTooltip(d2resource.Font16, d2resource.PaletteStatic, d2ui.TooltipXLeft, d2ui.TooltipYTop)
|
||||
|
||||
return &SkillPanel{
|
||||
asset: asset,
|
||||
activeSkill: activeSkill,
|
||||
ui: ui,
|
||||
isOpen: false,
|
||||
ListRows: make([]*SkillListRow, skillListsLength),
|
||||
renderer: ui.Renderer(),
|
||||
isLeftPanel: isLeftPanel,
|
||||
hero: hero,
|
||||
hoverTooltipText: hoverTooltipText,
|
||||
asset: asset,
|
||||
activeSkill: activeSkill,
|
||||
ui: ui,
|
||||
isOpen: false,
|
||||
ListRows: make([]*SkillListRow, skillListsLength),
|
||||
renderer: ui.Renderer(),
|
||||
isLeftPanel: isLeftPanel,
|
||||
hero: hero,
|
||||
hoverTooltip: hoverTooltip,
|
||||
}
|
||||
}
|
||||
|
||||
@ -143,18 +135,7 @@ func (s *SkillPanel) Render(target d2interface.Surface) error {
|
||||
}
|
||||
|
||||
if s.hoveredSkill != nil {
|
||||
target.PushTranslation(s.hoverTooltipRect.Left, s.hoverTooltipRect.Top)
|
||||
|
||||
black70 := d2util.Color(blackAlpha70)
|
||||
target.DrawRect(s.hoverTooltipRect.Width, s.hoverTooltipRect.Height, black70)
|
||||
|
||||
// the text should be centered horizontally in the tooltip rect
|
||||
centerX := s.hoverTooltipRect.Width >> 1
|
||||
target.PushTranslation(centerX, 0)
|
||||
s.hoverTooltipText.Render(target)
|
||||
|
||||
target.Pop()
|
||||
target.Pop()
|
||||
s.hoverTooltip.Render(target)
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -353,26 +334,13 @@ func (s *SkillPanel) HandleMouseMove(x, y int) bool {
|
||||
|
||||
if previousHovered != s.hoveredSkill && s.hoveredSkill != nil {
|
||||
skillDescription := d2tbl.TranslateString(s.hoveredSkill.ShortKey)
|
||||
s.hoverTooltipText.SetText(fmt.Sprintf("%s\n%s", s.hoveredSkill.Skill, skillDescription))
|
||||
s.hoverTooltip.SetText(fmt.Sprintf("%s\n%s", s.hoveredSkill.Skill, skillDescription))
|
||||
|
||||
listRow := s.GetListRowByPos(x, y)
|
||||
textWidth, textHeight := s.hoverTooltipText.GetSize()
|
||||
|
||||
tooltipX := (s.getSkillIdxAtPos(x, y) * skillIconWidth) + s.getRowStartX(listRow)
|
||||
tooltipWidth := textWidth + tooltipPadLeft + tooltipPadRight
|
||||
|
||||
if tooltipX+tooltipWidth >= screenWidth {
|
||||
tooltipX = screenWidth - tooltipWidth
|
||||
}
|
||||
|
||||
tooltipY := listRow.Rectangle.Top + listRow.Rectangle.Height
|
||||
tooltipHeight := textHeight + tooltipPadBottom
|
||||
|
||||
s.hoverTooltipRect = &d2geom.Rectangle{
|
||||
Left: tooltipX,
|
||||
Top: tooltipY,
|
||||
Width: tooltipWidth,
|
||||
Height: tooltipHeight}
|
||||
s.hoverTooltip.SetPosition(tooltipX, tooltipY)
|
||||
}
|
||||
|
||||
return true
|
||||
|
Loading…
Reference in New Issue
Block a user