diff --git a/d2core/d2ui/drawable.go b/d2core/d2ui/drawable.go index 277525b0..dc623ff9 100644 --- a/d2core/d2ui/drawable.go +++ b/d2core/d2ui/drawable.go @@ -14,4 +14,6 @@ type Drawable interface { OffsetPosition(xo, yo int) GetVisible() bool SetVisible(visible bool) + SetRenderPriority(priority RenderPriority) + GetRenderPriority() (priority RenderPriority) } diff --git a/d2core/d2ui/ui_manager.go b/d2core/d2ui/ui_manager.go index 5137a697..708cb02b 100644 --- a/d2core/d2ui/ui_manager.go +++ b/d2core/d2ui/ui_manager.go @@ -2,6 +2,7 @@ package d2ui import ( "log" + "sort" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" @@ -17,6 +18,7 @@ type UIManager struct { inputManager d2interface.InputManager audio d2interface.AudioProvider widgets []Widget + widgetsGroups []*WidgetGroup clickableWidgets []ClickableWidget cursorButtons CursorButton CursorX int @@ -49,6 +51,14 @@ func (ui *UIManager) Reset() { ui.pressedWidget = nil } +// addWidgetGroup adds a widgetGroup to the UI manager and sorts by priority +func (ui *UIManager) addWidgetGroup(group *WidgetGroup) { + ui.widgetsGroups = append(ui.widgetsGroups, group) + sort.SliceStable(ui.widgetsGroups, func(i, j int) bool { + return ui.widgetsGroups[i].priority < ui.widgetsGroups[j].priority + }) +} + // addWidget adds a widget to the UI manager func (ui *UIManager) addWidget(widget Widget) { err := ui.inputManager.BindHandler(widget) @@ -120,6 +130,15 @@ func (ui *UIManager) Render(target d2interface.Surface) { } } } + + for _, widgetGroup := range ui.widgetsGroups { + if widgetGroup.GetVisible() { + err := widgetGroup.Render(target) + if err != nil { + log.Print(err) + } + } + } } // contains determines whether a given x,y coordinate lands within a Widget diff --git a/d2core/d2ui/widget.go b/d2core/d2ui/widget.go index 5466583a..2c23959e 100644 --- a/d2core/d2ui/widget.go +++ b/d2core/d2ui/widget.go @@ -1,5 +1,16 @@ package d2ui +// RenderPriority determines in which order ui elements are drawn. +// The higher the number the later an element is drawn. +type RenderPriority int + +const ( + // RenderPriorityBackground is the first element drawn + RenderPriorityBackground RenderPriority = iota + // RenderPriorityForeground is the last element drawn + RenderPriorityForeground +) + // Widget defines an object that is a UI widget type Widget interface { Drawable @@ -19,23 +30,25 @@ type ClickableWidget interface { // BaseWidget contains default functionality that all widgets share type BaseWidget struct { - manager *UIManager - x int - y int - width int - height int - visible bool + manager *UIManager + x int + y int + width int + height int + renderPriority RenderPriority + visible bool } // NewBaseWidget creates a new BaseWidget with defaults func NewBaseWidget(manager *UIManager) *BaseWidget { return &BaseWidget{ - manager: manager, - x: 0, - y: 0, - width: 0, - height: 0, - visible: true, + manager: manager, + x: 0, + y: 0, + width: 0, + height: 0, + visible: true, + renderPriority: RenderPriorityBackground, } } @@ -73,3 +86,13 @@ func (b *BaseWidget) GetVisible() (visible bool) { func (b *BaseWidget) SetVisible(visible bool) { b.visible = visible } + +// GetRenderPriority returns the order in which this widget is rendered +func (b *BaseWidget) GetRenderPriority() (prio RenderPriority) { + return b.renderPriority +} + +// SetRenderPriority sets the order in which this widget is rendered +func (b *BaseWidget) SetRenderPriority(prio RenderPriority) { + b.renderPriority = prio +} diff --git a/d2core/d2ui/widget_group.go b/d2core/d2ui/widget_group.go new file mode 100644 index 00000000..da55cd63 --- /dev/null +++ b/d2core/d2ui/widget_group.go @@ -0,0 +1,92 @@ +package d2ui + +import ( + "sort" + + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" +) + +// static check that WidgetGroup implements widget +var _ Widget = &WidgetGroup{} + +// WidgetGroup allows the grouping of widgets to apply actions to all +// widgets at once. +type WidgetGroup struct { + *BaseWidget + entries []Widget + priority RenderPriority +} + +// NewWidgetGroup creates a new widget group +func (ui *UIManager) NewWidgetGroup(priority RenderPriority) *WidgetGroup { + base := NewBaseWidget(ui) + base.SetRenderPriority(priority) + + group := &WidgetGroup{ + BaseWidget: base, + } + + ui.addWidgetGroup(group) + + return group +} + +// AddWidget adds a widget to the group +func (wg *WidgetGroup) AddWidget(w Widget) { + wg.adjustSize(w) + wg.entries = append(wg.entries, w) + sort.SliceStable(wg.entries, func(i, j int) bool { + return wg.entries[i].GetRenderPriority() < wg.entries[j].GetRenderPriority() + }) +} + +// adjustSize recalculates the bounding box if a new widget is added +func (wg *WidgetGroup) adjustSize(w Widget) { + x, y := w.GetPosition() + width, height := w.GetSize() + + if x+width > wg.width { + wg.width = x + width + } + + if wg.x > x { + wg.width += wg.x - x + wg.x = x + } + + if y+height > wg.height { + wg.height = x + height + } + + if wg.y > y { + wg.height += wg.y - y + wg.y = y + } +} + +// Advance is a no-op here +func (wg *WidgetGroup) Advance(elapsed float64) error { + // No-op + return nil +} + +// Render draw the widgets to the screen +func (wg *WidgetGroup) Render(target d2interface.Surface) error { + for _, entry := range wg.entries { + if entry.GetVisible() { + err := entry.Render(target) + if err != nil { + return err + } + } + } + + return nil +} + +// SetVisible sets the visibility of all widgets in the group +func (wg *WidgetGroup) SetVisible(visible bool) { + for _, entry := range wg.entries { + entry.SetVisible(visible) + } +}