From f4f749d0d2434a3dfb6babd04c3d56868de9cb94 Mon Sep 17 00:00:00 2001 From: Louis Jones Date: Sat, 21 Mar 2026 15:05:40 +0000 Subject: [PATCH 1/6] WIP --- config.go | 53 ++++++++++++++++++++++++++++++++++++--------------- key.go | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- lcdknob.go | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 145 insertions(+), 18 deletions(-) diff --git a/config.go b/config.go index 514f1de..7b71993 100644 --- a/config.go +++ b/config.go @@ -23,6 +23,25 @@ type KeyGridBackgrounder interface { GetKeyGridBackgroundHandlerFields() map[string]any } +type InputActions interface { + GetSwitchPage() int + GetKeyBind() string + GetCommand() string + GetBrightness() int + GetUrl() string + GetObsCommand() string + GetObsCommandParams() map[string]string +} + +type ForegroundActions interface { + GetIcon() string + GetText() string + GetTextSize() int + GetTextAlignment() VerticalAlignment + GetFontFace() string + GetTextColour() string +} + type ConfigV3 struct { Modules []string `json:"modules,omitempty"` Decks []DeckV3 `json:"decks"` @@ -152,21 +171,25 @@ func (p *PageV3) GetKeyGridBackgroundHandlerFields() map[string]any { } type StreamDeckInfoV1 struct { - Cols int `json:"cols,omitempty"` - Rows int `json:"rows,omitempty"` - IconSize int `json:"icon_size,omitempty"` - Page int `json:"page"` - Serial string `json:"serial,omitempty"` - Name string `json:"name,omitempty"` - Connected bool `json:"connected"` - LastConnected time.Time `json:"last_connected,omitempty"` - LastDisconnected time.Time `json:"last_disconnected,omitempty"` - LcdWidth int `json:"lcd_width,omitempty"` - LcdHeight int `json:"lcd_height,omitempty"` - LcdCols int `json:"lcd_cols,omitempty"` - KnobCols int `json:"knob_cols,omitempty"` - PaddingX int `json:"padding_x"` - PaddingY int `json:"padding_y"` + Cols int `json:"cols,omitempty"` + Rows int `json:"rows,omitempty"` + IconSize int `json:"icon_size,omitempty"` + Page int `json:"page"` + Serial string `json:"serial,omitempty"` + Name string `json:"name,omitempty"` + Connected bool `json:"connected"` + LastConnected time.Time `json:"last_connected,omitempty"` + LastDisconnected time.Time `json:"last_disconnected,omitempty"` + LcdWidth int `json:"lcd_width,omitempty"` + LcdHeight int `json:"lcd_height,omitempty"` + LcdCols int `json:"lcd_cols,omitempty"` + KnobCols int `json:"knob_cols,omitempty"` + PaddingX int `json:"padding_x"` + PaddingY int `json:"padding_y"` + KeyGridBackgroundWidth int `json:"key_grid_background_width"` + KeyGridBackgroundHeight int `json:"key_grid_background_height"` + LcdBackgroundWidth int `json:"lcd_background_width"` + LcdBackgroundHeight int `json:"lcd_background_height"` } type ObsConnectionInfoV2 struct { diff --git a/key.go b/key.go index f515095..552b42c 100644 --- a/key.go +++ b/key.go @@ -49,12 +49,12 @@ func (k *KeyV3) GetKeyBackgroundHandler() KeyGridBackgroundHandler { type KeyConfigV3 struct { Icon string `json:"icon,omitempty"` - SwitchPage int `json:"switch_page,omitempty"` Text string `json:"text,omitempty"` TextSize int `json:"text_size,omitempty"` - TextAlignment string `json:"text_alignment,omitempty"` + TextAlignment VerticalAlignment `json:"text_alignment,omitempty"` FontFace string `json:"font_face,omitempty"` TextColour string `json:"text_colour,omitempty"` + SwitchPage int `json:"switch_page,omitempty"` Keybind string `json:"keybind,omitempty"` Command string `json:"command,omitempty"` Brightness int `json:"brightness,omitempty"` @@ -76,6 +76,58 @@ type KeyConfigV3 struct { KeyBackgroundHandlerFields map[string]any `json:"key_background_handler_fields"` } +func (kc *KeyConfigV3) GetIcon() string { + return kc.Icon +} + +func (kc *KeyConfigV3) GetText() string { + return kc.Text +} + +func (kc *KeyConfigV3) GetTextSize() int { + return kc.TextSize +} + +func (kc *KeyConfigV3) GetTextAlignment() VerticalAlignment { + return kc.TextAlignment +} + +func (kc *KeyConfigV3) GetFontFace() string { + return kc.FontFace +} + +func (kc *KeyConfigV3) GetTextColour() string { + return kc.TextColour +} + +func (kc *KeyConfigV3) GetSwitchPage() int { + return kc.SwitchPage +} + +func (kc *KeyConfigV3) GetKeyBind() string { + return kc.Keybind +} + +func (kc *KeyConfigV3) GetCommand() string { + return kc.Command +} + +func (kc *KeyConfigV3) GetBrightness() int { + return kc.Brightness +} + +func (kc *KeyConfigV3) GetUrl() string { + return kc.Url +} + +func (kc *KeyConfigV3) GetObsCommand() string { + return kc.ObsCommand +} + +func (kc *KeyConfigV3) GetObsCommandParams() map[string]string { + return kc.ObsCommandParams +} + func (kc *KeyConfigV3) GetKeyBackgroundHandlerFields() map[string]any { return kc.KeyBackgroundHandlerFields } diff --git a/lcdknob.go b/lcdknob.go index 049e030..c00999f 100644 --- a/lcdknob.go +++ b/lcdknob.go @@ -55,11 +55,39 @@ type KnobActionV3 struct { ObsCommandParams map[string]string `json:"obs_command_params,omitempty"` } +func (k *KnobActionV3) GetSwitchPage() int { + return k.SwitchPage +} + +func (k *KnobActionV3) GetKeyBind() string { + return k.Keybind +} + +func (k *KnobActionV3) GetCommand() string { + return k.Command +} + +func (k *KnobActionV3) GetBrightness() int { + return k.Brightness +} + +func (k *KnobActionV3) GetUrl() string { + return k.Url +} + +func (k *KnobActionV3) GetObsCommand() string { + return k.ObsCommand +} + +func (k *KnobActionV3) GetObsCommandParams() map[string]string { + return k.ObsCommandParams +} + type KnobConfigV3 struct { Icon string `json:"icon,omitempty"` Text string `json:"text,omitempty"` TextSize int `json:"text_size,omitempty"` - TextAlignment string `json:"text_alignment,omitempty"` + TextAlignment VerticalAlignment `json:"text_alignment,omitempty"` FontFace string `json:"font_face,omitempty"` TextColour string `json:"text_colour,omitempty"` LcdHandler string `json:"lcd_handler,omitempty"` @@ -79,6 +107,30 @@ type KnobConfigV3 struct { TouchPanelBackgroundHandlerFields map[string]any `json:"touch_panel_background_handler_fields"` } +func (kc *KnobConfigV3) GetIcon() string { + return kc.Icon +} + +func (kc *KnobConfigV3) GetText() string { + return kc.Text +} + +func (kc *KnobConfigV3) GetTextSize() int { + return kc.TextSize +} + +func (kc *KnobConfigV3) GetTextAlignment() VerticalAlignment { + return kc.TextAlignment +} + +func (kc *KnobConfigV3) GetFontFace() string { + return kc.FontFace +} + +func (kc *KnobConfigV3) GetTextColour() string { + return kc.TextColour +} + func (kc *KnobConfigV3) GetTouchPanelBackgroundHandlerFields() map[string]any { return kc.TouchPanelBackgroundHandlerFields } From a491204a88bb1522509167e73f4b28805740fb4d Mon Sep 17 00:00:00 2001 From: The-Jonsey Date: Sat, 21 Mar 2026 19:02:08 +0000 Subject: [PATCH 2/6] WIP --- Module.go | 33 ++++++++------------- config.go | 86 +++++++++++++++++++++++++++++++----------------------- handler.go | 39 +++++++++---------------- key.go | 82 +++++++++++++++++++++++++-------------------------- lcdknob.go | 68 +++++++++++++++++++++--------------------- 5 files changed, 150 insertions(+), 158 deletions(-) diff --git a/Module.go b/Module.go index 3dde72d..ad0673f 100644 --- a/Module.go +++ b/Module.go @@ -3,29 +3,20 @@ package api type Module struct { Name string `json:"name,omitempty"` - NewIcon func() IconHandler `json:"-"` - NewKey func() KeyHandler `json:"-"` - NewLcd func() LcdHandler `json:"-"` - NewKnobOrTouch func() KnobOrTouchHandler `json:"-"` - NewKeyGridBackground func() KeyGridBackgroundHandler `json:"-"` - NewTouchPanelBackgroundHandler func() TouchPanelBackgroundHandler `json:"-"` + NewForeground func() ForegroundHandler `json:"-"` + NewInput func() InputHandler `json:"-"` + NewBackground func() BackgroundHandler `json:"-"` - IconFields []Field `json:"icon_fields,omitempty"` - KeyFields []Field `json:"key_fields,omitempty"` - LcdFields []Field `json:"lcd_fields,omitempty"` - KnobOrTouchFields []Field `json:"knob_or_touch_fields,omitempty"` - LinkedFields []Field `json:"linked_fields,omitempty"` - KeyGridBackgroundFields []Field `json:"key_grid_background_fields"` - TouchPanelBackgroundFields []Field `json:"touch_panel_background_fields"` + ForegroundFields []Field `json:"icon_fields,omitempty"` + InputFields []Field `json:"key_fields,omitempty"` + BackgroundFields []Field `json:"lcd_fields,omitempty"` + LinkedFields []Field `json:"linked_fields,omitempty"` - IsIcon bool `json:"is_icon,omitempty"` - IsKey bool `json:"is_key,omitempty"` - IsLcd bool `json:"is_lcd,omitempty"` - IsKnob bool `json:"is_knob,omitempty"` - IsLinkedHandlers bool `json:"is_linked_handlers,omitempty"` - IsKeyGridBackground bool `json:"is_key_grid_background"` - IsTouchPanelBackground bool `json:"is_touch_panel_background"` - Linked bool `json:"linked,omitempty"` + IsForeground bool `json:"is_foreground,omitempty"` + IsInput bool `json:"is_input,omitempty"` + IsBackground bool `json:"is_background,omitempty"` + IsLinkedHandlers bool `json:"is_linked_handlers,omitempty"` + Linked bool `json:"linked,omitempty"` } type FieldType string diff --git a/config.go b/config.go index 7b71993..4536576 100644 --- a/config.go +++ b/config.go @@ -9,8 +9,8 @@ type LcdBackgrounder interface { GetTouchPanelBackground() string GetTouchPanelBackgroundBuff() []image.Image SetTouchPanelBackgroundBuff(img []image.Image) - GetTouchPanelBackgroundHandler() TouchPanelBackgroundHandler - SetTouchPanelBackgroundHandler(handler TouchPanelBackgroundHandler) + GetTouchPanelBackgroundHandler() BackgroundHandler + SetTouchPanelBackgroundHandler(handler BackgroundHandler) GetTouchPanelBackgroundHandlerFields() map[string]any } @@ -18,8 +18,8 @@ type KeyGridBackgrounder interface { GetKeyGridBackground() string GetKeyGridBackgroundBuff() []image.Image SetKeyGridBackgroundBuff(img []image.Image) - GetKeyGridBackgroundHandler() KeyGridBackgroundHandler - SetKeyGridBackgroundHandler(handler KeyGridBackgroundHandler) + GetKeyGridBackgroundHandler() BackgroundHandler + SetKeyGridBackgroundHandler(handler BackgroundHandler) GetKeyGridBackgroundHandlerFields() map[string]any } @@ -49,23 +49,23 @@ type ConfigV3 struct { } type DeckV3 struct { - Serial string `json:"serial"` - Pages []PageV3 `json:"pages"` - TouchPanelBackground string `json:"touch_panel_background"` - TouchPanelBackgroundBuff []image.Image `json:"-"` - TouchPanelBackgroundHandler TouchPanelBackgroundHandler `json:"-"` - TouchPanelBackgroundHandlerFields map[string]any `json:"touch_panel_background_handler_fields"` - KeyGridBackground string `json:"key_grid_background"` - KeyGridBackgroundBuff []image.Image `json:"-"` - KeyGridBackgroundHandler KeyGridBackgroundHandler `json:"-"` - KeyGridBackgroundHandlerFields map[string]any `json:"key_grid_background_handler_fields"` -} - -func (d *DeckV3) SetTouchPanelBackgroundHandler(handler TouchPanelBackgroundHandler) { + Serial string `json:"serial"` + Pages []PageV3 `json:"pages"` + TouchPanelBackground string `json:"touch_panel_background"` + TouchPanelBackgroundBuff []image.Image `json:"-"` + TouchPanelBackgroundHandler BackgroundHandler `json:"-"` + TouchPanelBackgroundHandlerFields map[string]any `json:"touch_panel_background_handler_fields"` + KeyGridBackground string `json:"key_grid_background"` + KeyGridBackgroundBuff []image.Image `json:"-"` + KeyGridBackgroundHandler BackgroundHandler `json:"-"` + KeyGridBackgroundHandlerFields map[string]any `json:"key_grid_background_handler_fields"` +} + +func (d *DeckV3) SetTouchPanelBackgroundHandler(handler BackgroundHandler) { d.TouchPanelBackgroundHandler = handler } -func (d *DeckV3) GetTouchPanelBackgroundHandler() TouchPanelBackgroundHandler { +func (d *DeckV3) GetTouchPanelBackgroundHandler() BackgroundHandler { return d.TouchPanelBackgroundHandler } @@ -97,11 +97,11 @@ func (d *DeckV3) SetKeyGridBackgroundBuff(img []image.Image) { d.KeyGridBackgroundBuff = img } -func (d *DeckV3) SetKeyGridBackgroundHandler(handler KeyGridBackgroundHandler) { +func (d *DeckV3) SetKeyGridBackgroundHandler(handler BackgroundHandler) { d.KeyGridBackgroundHandler = handler } -func (d *DeckV3) GetKeyGridBackgroundHandler() KeyGridBackgroundHandler { +func (d *DeckV3) GetKeyGridBackgroundHandler() BackgroundHandler { return d.KeyGridBackgroundHandler } @@ -110,23 +110,23 @@ func (d *DeckV3) GetKeyGridBackgroundHandlerFields() map[string]any { } type PageV3 struct { - Keys []KeyV3 `json:"keys"` - Knobs []KnobV3 `json:"knobs"` - TouchPanelBackground string `json:"touch_panel_background"` - TouchPanelBackgroundBuff []image.Image `json:"-"` - TouchPanelBackgroundHandler TouchPanelBackgroundHandler `json:"-"` - TouchPanelBackgroundHandlerFields map[string]any `json:"touch_panel_background_handler_fields"` - KeyGridBackground string `json:"key_grid_background"` - KeyGridBackgroundBuff []image.Image `json:"-"` - KeyGridBackgroundHandler KeyGridBackgroundHandler `json:"-"` - KeyGridBackgroundHandlerFields map[string]any `json:"key_grid_background_handler_fields"` -} - -func (p *PageV3) SetTouchPanelBackgroundHandler(handler TouchPanelBackgroundHandler) { + Keys []KeyV3 `json:"keys"` + Knobs []KnobV3 `json:"knobs"` + TouchPanelBackground string `json:"touch_panel_background"` + TouchPanelBackgroundBuff []image.Image `json:"-"` + TouchPanelBackgroundHandler BackgroundHandler `json:"-"` + TouchPanelBackgroundHandlerFields map[string]any `json:"touch_panel_background_handler_fields"` + KeyGridBackground string `json:"key_grid_background"` + KeyGridBackgroundBuff []image.Image `json:"-"` + KeyGridBackgroundHandler BackgroundHandler `json:"-"` + KeyGridBackgroundHandlerFields map[string]any `json:"key_grid_background_handler_fields"` +} + +func (p *PageV3) SetTouchPanelBackgroundHandler(handler BackgroundHandler) { p.TouchPanelBackgroundHandler = handler } -func (p *PageV3) GetTouchPanelBackgroundHandler() TouchPanelBackgroundHandler { +func (p *PageV3) GetTouchPanelBackgroundHandler() BackgroundHandler { return p.TouchPanelBackgroundHandler } @@ -158,11 +158,11 @@ func (p *PageV3) SetKeyGridBackgroundBuff(img []image.Image) { p.KeyGridBackgroundBuff = img } -func (p *PageV3) SetKeyGridBackgroundHandler(handler KeyGridBackgroundHandler) { +func (p *PageV3) SetKeyGridBackgroundHandler(handler BackgroundHandler) { p.KeyGridBackgroundHandler = handler } -func (p *PageV3) GetKeyGridBackgroundHandler() KeyGridBackgroundHandler { +func (p *PageV3) GetKeyGridBackgroundHandler() BackgroundHandler { return p.KeyGridBackgroundHandler } @@ -192,6 +192,20 @@ type StreamDeckInfoV1 struct { LcdBackgroundHeight int `json:"lcd_background_height"` } +func (info StreamDeckInfoV1) GetDimensions(handlerType HandlerType) (int, int) { + if handlerType == LCD { + return info.LcdWidth, info.LcdHeight + } + return info.IconSize, info.IconSize +} + +func (info StreamDeckInfoV1) GetGridDimensions(handlerType HandlerType) (int, int) { + if handlerType == LCD { + return info.LcdBackgroundHeight, info.LcdBackgroundHeight + } + return info.KeyGridBackgroundWidth, info.KeyGridBackgroundHeight +} + type ObsConnectionInfoV2 struct { Host string `json:"host,omitempty"` Port int `json:"port,omitempty"` diff --git a/handler.go b/handler.go index 40db4bf..06c0e4f 100644 --- a/handler.go +++ b/handler.go @@ -14,41 +14,25 @@ type VisualHandler interface { type InputHandler interface { Handler + Input(fields map[string]any, handlerType HandlerType, info StreamDeckInfoV1, event InputEvent) } -type IconHandler interface { +type ForegroundHandler interface { VisualHandler - Start(key KeyConfigV3, info StreamDeckInfoV1, callback func(image image.Image)) -} - -type KeyHandler interface { - InputHandler - Key(key KeyConfigV3, info StreamDeckInfoV1) -} - -type LcdHandler interface { - VisualHandler - Start(key KnobConfigV3, info StreamDeckInfoV1, callback func(image image.Image)) -} - -type KnobOrTouchHandler interface { - InputHandler - Input(key KnobConfigV3, info StreamDeckInfoV1, event InputEvent) + Start(fields map[string]any, handlerType HandlerType, info StreamDeckInfoV1, callback func(image image.Image)) } type BackgroundHandler interface { - VisualHandler - Start(fields map[string]any, info StreamDeckInfoV1, callback func(images []image.Image)) - StartIndividual(fields map[string]any, info StreamDeckInfoV1, callback func(img image.Image)) + ForegroundHandler + StartGrid(fields map[string]any, handlerType HandlerType, info StreamDeckInfoV1, callback func(images []image.Image)) } -type KeyGridBackgroundHandler interface { - BackgroundHandler -} +type HandlerType uint8 -type TouchPanelBackgroundHandler interface { - BackgroundHandler -} +const ( + LCD HandlerType = iota + KEY +) type InputEventType uint8 @@ -58,6 +42,9 @@ const ( KNOB_PRESS SCREEN_SHORT_TAP SCREEN_LONG_TAP + SCREEN_SWIPE + KEY_PRESS + KEY_RELEASE ) type InputEvent struct { diff --git a/key.go b/key.go index 552b42c..8c69376 100644 --- a/key.go +++ b/key.go @@ -6,21 +6,21 @@ type KeyBackgrounder interface { GetKeyBackground() string GetKeyBackgroundBuff() image.Image SetKeyBackgroundBuff(img image.Image) - GetKeyBackgroundHandler() KeyGridBackgroundHandler - SetKeyBackgroundHandler(handler KeyGridBackgroundHandler) + GetKeyBackgroundHandler() BackgroundHandler + SetKeyBackgroundHandler(handler BackgroundHandler) GetKeyBackgroundHandlerFields() map[string]any } type KeyV3 struct { - Application map[string]*KeyConfigV3 `json:"application,omitempty"` - ActiveBuff image.Image `json:"-"` - ActiveIconHandlerStruct *IconHandler `json:"-"` - ActiveKeyHandlerStruct *KeyHandler `json:"-"` - ActiveApplication string `json:"-"` - KeyBackground string `json:"background"` - KeyBackgroundBuff image.Image `json:"-"` - KeyBackgroundHandler KeyGridBackgroundHandler `json:"-"` - KeyBackgroundHandlerFields map[string]any `json:"key_background_handler_fields"` + Application map[string]*KeyConfigV3 `json:"application,omitempty"` + ActiveBuff image.Image `json:"-"` + ActiveIconHandlerStruct *ForegroundHandler `json:"-"` + ActiveKeyHandlerStruct *InputHandler `json:"-"` + ActiveApplication string `json:"-"` + KeyBackground string `json:"background"` + KeyBackgroundBuff image.Image `json:"-"` + KeyBackgroundHandler BackgroundHandler `json:"-"` + KeyBackgroundHandlerFields map[string]any `json:"key_background_handler_fields"` } func (k *KeyV3) GetKeyBackgroundHandlerFields() map[string]any { @@ -39,41 +39,41 @@ func (k *KeyV3) SetKeyBackgroundBuff(img image.Image) { k.KeyBackgroundBuff = img } -func (k *KeyV3) SetKeyBackgroundHandler(handler KeyGridBackgroundHandler) { +func (k *KeyV3) SetKeyBackgroundHandler(handler BackgroundHandler) { k.KeyBackgroundHandler = handler } -func (k *KeyV3) GetKeyBackgroundHandler() KeyGridBackgroundHandler { +func (k *KeyV3) GetKeyBackgroundHandler() BackgroundHandler { return k.KeyBackgroundHandler } type KeyConfigV3 struct { - Icon string `json:"icon,omitempty"` - Text string `json:"text,omitempty"` - TextSize int `json:"text_size,omitempty"` - TextAlignment VerticalAlignment `json:"text_alignment,omitempty"` - FontFace string `json:"font_face,omitempty"` - TextColour string `json:"text_colour,omitempty"` - SwitchPage int `json:"switch_page,omitempty"` - Keybind string `json:"keybind,omitempty"` - Command string `json:"command,omitempty"` - Brightness int `json:"brightness,omitempty"` - Url string `json:"url,omitempty"` - KeyHold int `json:"key_hold,omitempty"` - ObsCommand string `json:"obs_command,omitempty"` - ObsCommandParams map[string]string `json:"obs_command_params,omitempty"` - IconHandler string `json:"icon_handler,omitempty"` - KeyHandler string `json:"key_handler,omitempty"` - IconHandlerFields map[string]any `json:"icon_handler_fields,omitempty"` - KeyHandlerFields map[string]any `json:"key_handler_fields,omitempty"` - SharedHandlerFields map[string]any `json:"shared_handler_fields,omitempty"` - IconHandlerStruct IconHandler `json:"-"` - KeyHandlerStruct KeyHandler `json:"-"` - SharedState map[string]any `json:"-"` - KeyBackground string `json:"background"` - KeyBackgroundBuff image.Image `json:"-"` - KeyBackgroundHandler KeyGridBackgroundHandler `json:"-"` - KeyBackgroundHandlerFields map[string]any `json:"key_background_handler_fields"` + Icon string `json:"icon,omitempty"` + Text string `json:"text,omitempty"` + TextSize int `json:"text_size,omitempty"` + TextAlignment VerticalAlignment `json:"text_alignment,omitempty"` + FontFace string `json:"font_face,omitempty"` + TextColour string `json:"text_colour,omitempty"` + SwitchPage int `json:"switch_page,omitempty"` + Keybind string `json:"keybind,omitempty"` + Command string `json:"command,omitempty"` + Brightness int `json:"brightness,omitempty"` + Url string `json:"url,omitempty"` + KeyHold int `json:"key_hold,omitempty"` + ObsCommand string `json:"obs_command,omitempty"` + ObsCommandParams map[string]string `json:"obs_command_params,omitempty"` + IconHandler string `json:"icon_handler,omitempty"` + KeyHandler string `json:"key_handler,omitempty"` + IconHandlerFields map[string]any `json:"icon_handler_fields,omitempty"` + KeyHandlerFields map[string]any `json:"key_handler_fields,omitempty"` + SharedHandlerFields map[string]any `json:"shared_handler_fields,omitempty"` + IconHandlerStruct ForegroundHandler `json:"-"` + KeyHandlerStruct InputHandler `json:"-"` + SharedState map[string]any `json:"-"` + KeyBackground string `json:"background"` + KeyBackgroundBuff image.Image `json:"-"` + KeyBackgroundHandler BackgroundHandler `json:"-"` + KeyBackgroundHandlerFields map[string]any `json:"key_background_handler_fields"` } func (kc *KeyConfigV3) GetIcon() string { @@ -144,10 +144,10 @@ func (kc *KeyConfigV3) SetKeyBackgroundBuff(img image.Image) { kc.KeyBackgroundBuff = img } -func (kc *KeyConfigV3) SetKeyBackgroundHandler(handler KeyGridBackgroundHandler) { +func (kc *KeyConfigV3) SetKeyBackgroundHandler(handler BackgroundHandler) { kc.KeyBackgroundHandler = handler } -func (kc *KeyConfigV3) GetKeyBackgroundHandler() KeyGridBackgroundHandler { +func (kc *KeyConfigV3) GetKeyBackgroundHandler() BackgroundHandler { return kc.KeyBackgroundHandler } diff --git a/lcdknob.go b/lcdknob.go index c00999f..9d07554 100644 --- a/lcdknob.go +++ b/lcdknob.go @@ -6,30 +6,30 @@ type LcdSegmentBackgrounder interface { GetTouchPanelBackground() string GetTouchPanelBackgroundBuff() image.Image SetTouchPanelBackgroundBuff(img image.Image) - GetTouchPanelBackgroundHandler() TouchPanelBackgroundHandler - SetTouchPanelBackgroundHandler(handler TouchPanelBackgroundHandler) + GetTouchPanelBackgroundHandler() ForegroundHandler + SetTouchPanelBackgroundHandler(handler ForegroundHandler) GetTouchPanelBackgroundHandlerFields() map[string]any } type KnobV3 struct { - Application map[string]*KnobConfigV3 `json:"application,omitempty"` - ActiveBuff image.Image `json:"-"` - ActiveApplication string `json:"-"` - TouchPanelBackground string `json:"touch_panel_background"` - TouchPanelBackgroundBuff image.Image `json:"-"` - TouchPanelBackgroundHandler TouchPanelBackgroundHandler `json:"-"` - TouchPanelBackgroundHandlerFields map[string]any `json:"touch_panel_background_handler_fields"` + Application map[string]*KnobConfigV3 `json:"application,omitempty"` + ActiveBuff image.Image `json:"-"` + ActiveApplication string `json:"-"` + TouchPanelBackground string `json:"touch_panel_background"` + TouchPanelBackgroundBuff image.Image `json:"-"` + TouchPanelBackgroundHandler ForegroundHandler `json:"-"` + TouchPanelBackgroundHandlerFields map[string]any `json:"touch_panel_background_handler_fields"` } func (k *KnobV3) GetTouchPanelBackgroundHandlerFields() map[string]any { return k.TouchPanelBackgroundHandlerFields } -func (k *KnobV3) SetTouchPanelBackgroundHandler(handler TouchPanelBackgroundHandler) { +func (k *KnobV3) SetTouchPanelBackgroundHandler(handler ForegroundHandler) { k.TouchPanelBackgroundHandler = handler } -func (k *KnobV3) GetTouchPanelBackgroundHandler() TouchPanelBackgroundHandler { +func (k *KnobV3) GetTouchPanelBackgroundHandler() ForegroundHandler { return k.TouchPanelBackgroundHandler } @@ -84,27 +84,27 @@ func (k *KnobActionV3) GetObsCommandParams() map[string]string { } type KnobConfigV3 struct { - Icon string `json:"icon,omitempty"` - Text string `json:"text,omitempty"` - TextSize int `json:"text_size,omitempty"` - TextAlignment VerticalAlignment `json:"text_alignment,omitempty"` - FontFace string `json:"font_face,omitempty"` - TextColour string `json:"text_colour,omitempty"` - LcdHandler string `json:"lcd_handler,omitempty"` - KnobOrTouchHandler string `json:"knob_or_touch_handler,omitempty"` - LcdHandlerStruct LcdHandler `json:"-"` - KnobOrTouchHandlerStruct KnobOrTouchHandler `json:"-"` - LcdHandlerFields map[string]any `json:"lcd_handler_fields,omitempty"` - KnobOrTouchHandlerFields map[string]any `json:"knob_or_touch_handler_fields,omitempty"` - SharedHandlerFields map[string]any `json:"shared_handler_fields,omitempty"` - KnobPressAction KnobActionV3 `json:"knob_press_action,omitempty"` - KnobTurnUpAction KnobActionV3 `json:"knob_turn_up_action,omitempty"` - KnobTurnDownAction KnobActionV3 `json:"knob_turn_down_action,omitempty"` - SharedState map[string]any `json:"-"` - TouchPanelBackground string `json:"touch_panel_background"` - TouchPanelBackgroundBuff image.Image `json:"-"` - TouchPanelBackgroundHandler TouchPanelBackgroundHandler `json:"-"` - TouchPanelBackgroundHandlerFields map[string]any `json:"touch_panel_background_handler_fields"` + Icon string `json:"icon,omitempty"` + Text string `json:"text,omitempty"` + TextSize int `json:"text_size,omitempty"` + TextAlignment VerticalAlignment `json:"text_alignment,omitempty"` + FontFace string `json:"font_face,omitempty"` + TextColour string `json:"text_colour,omitempty"` + LcdHandler string `json:"lcd_handler,omitempty"` + KnobOrTouchHandler string `json:"knob_or_touch_handler,omitempty"` + LcdHandlerStruct ForegroundHandler `json:"-"` + KnobOrTouchHandlerStruct InputHandler `json:"-"` + LcdHandlerFields map[string]any `json:"lcd_handler_fields,omitempty"` + KnobOrTouchHandlerFields map[string]any `json:"knob_or_touch_handler_fields,omitempty"` + SharedHandlerFields map[string]any `json:"shared_handler_fields,omitempty"` + KnobPressAction KnobActionV3 `json:"knob_press_action,omitempty"` + KnobTurnUpAction KnobActionV3 `json:"knob_turn_up_action,omitempty"` + KnobTurnDownAction KnobActionV3 `json:"knob_turn_down_action,omitempty"` + SharedState map[string]any `json:"-"` + TouchPanelBackground string `json:"touch_panel_background"` + TouchPanelBackgroundBuff image.Image `json:"-"` + TouchPanelBackgroundHandler ForegroundHandler `json:"-"` + TouchPanelBackgroundHandlerFields map[string]any `json:"touch_panel_background_handler_fields"` } func (kc *KnobConfigV3) GetIcon() string { @@ -135,11 +135,11 @@ func (kc *KnobConfigV3) GetTouchPanelBackgroundHandlerFields() map[string]any { return kc.TouchPanelBackgroundHandlerFields } -func (kc *KnobConfigV3) SetTouchPanelBackgroundHandler(handler TouchPanelBackgroundHandler) { +func (kc *KnobConfigV3) SetTouchPanelBackgroundHandler(handler ForegroundHandler) { kc.TouchPanelBackgroundHandler = handler } -func (kc *KnobConfigV3) GetTouchPanelBackgroundHandler() TouchPanelBackgroundHandler { +func (kc *KnobConfigV3) GetTouchPanelBackgroundHandler() ForegroundHandler { return kc.TouchPanelBackgroundHandler } From be1bb3387709d70750624aa925f3fb1f8fcf4983 Mon Sep 17 00:00:00 2001 From: Louis Jones Date: Sat, 21 Mar 2026 21:28:44 +0000 Subject: [PATCH 3/6] Add combo handlers, and split background image method --- config.go | 30 +++++++++++++++++++++++++++++- handler.go | 5 +++++ key.go | 1 - lcdknob.go | 1 - obj.go | 47 ----------------------------------------------- 5 files changed, 34 insertions(+), 50 deletions(-) delete mode 100644 obj.go diff --git a/config.go b/config.go index 4536576..3c21822 100644 --- a/config.go +++ b/config.go @@ -2,6 +2,7 @@ package api import ( "image" + "math" "time" ) @@ -201,11 +202,38 @@ func (info StreamDeckInfoV1) GetDimensions(handlerType HandlerType) (int, int) { func (info StreamDeckInfoV1) GetGridDimensions(handlerType HandlerType) (int, int) { if handlerType == LCD { - return info.LcdBackgroundHeight, info.LcdBackgroundHeight + return info.LcdBackgroundWidth, info.LcdBackgroundHeight } return info.KeyGridBackgroundWidth, info.KeyGridBackgroundHeight } +func (info StreamDeckInfoV1) SplitBackgroundImage(background image.Image, handlerType HandlerType) []image.Image { + var frameArr []image.Image + + if handlerType == KEY { + for keyIndex := range info.Cols * info.Rows { + keyX := keyIndex % info.Cols + keyY := int(math.Floor(float64(keyIndex) / float64(info.Cols))) + + x0, y0 := keyX*(info.IconSize+info.PaddingX), keyY*(info.IconSize+info.PaddingY) + x1, y1 := keyX*(info.IconSize+info.PaddingX)+info.IconSize, keyY*(info.IconSize+info.PaddingY)+info.IconSize + + frameArr = append(frameArr, SubImage(background, x0, y0, x1, y1)) + } + } else { + for lcdIndex := range info.LcdCols { + x0, y0 := info.LcdWidth*lcdIndex, 0 + x1, y1 := info.LcdWidth*(lcdIndex+1), info.LcdHeight + + subImage := SubImage(background, x0, y0, x1, y1) + + frameArr = append(frameArr, subImage) + } + } + + return frameArr +} + type ObsConnectionInfoV2 struct { Host string `json:"host,omitempty"` Port int `json:"port,omitempty"` diff --git a/handler.go b/handler.go index 06c0e4f..9e45be2 100644 --- a/handler.go +++ b/handler.go @@ -22,6 +22,11 @@ type ForegroundHandler interface { Start(fields map[string]any, handlerType HandlerType, info StreamDeckInfoV1, callback func(image image.Image)) } +type CombinedHandler interface { + VisualHandler + InputHandler +} + type BackgroundHandler interface { ForegroundHandler StartGrid(fields map[string]any, handlerType HandlerType, info StreamDeckInfoV1, callback func(images []image.Image)) diff --git a/key.go b/key.go index 8c69376..ddae2fd 100644 --- a/key.go +++ b/key.go @@ -69,7 +69,6 @@ type KeyConfigV3 struct { SharedHandlerFields map[string]any `json:"shared_handler_fields,omitempty"` IconHandlerStruct ForegroundHandler `json:"-"` KeyHandlerStruct InputHandler `json:"-"` - SharedState map[string]any `json:"-"` KeyBackground string `json:"background"` KeyBackgroundBuff image.Image `json:"-"` KeyBackgroundHandler BackgroundHandler `json:"-"` diff --git a/lcdknob.go b/lcdknob.go index 9d07554..95d9f35 100644 --- a/lcdknob.go +++ b/lcdknob.go @@ -100,7 +100,6 @@ type KnobConfigV3 struct { KnobPressAction KnobActionV3 `json:"knob_press_action,omitempty"` KnobTurnUpAction KnobActionV3 `json:"knob_turn_up_action,omitempty"` KnobTurnDownAction KnobActionV3 `json:"knob_turn_down_action,omitempty"` - SharedState map[string]any `json:"-"` TouchPanelBackground string `json:"touch_panel_background"` TouchPanelBackgroundBuff image.Image `json:"-"` TouchPanelBackgroundHandler ForegroundHandler `json:"-"` diff --git a/obj.go b/obj.go deleted file mode 100644 index 303758d..0000000 --- a/obj.go +++ /dev/null @@ -1,47 +0,0 @@ -package api - -import "image" - -type StreamDeckInfo struct { - Cols int `json:"cols,omitempty"` - Rows int `json:"rows,omitempty"` - IconSize int `json:"icon_size,omitempty"` - Page int `json:"page"` - Serial string `json:"serial,omitempty"` -} - -type Page []Key - -type Deck struct { - Serial string `json:"serial"` - Pages []Page `json:"pages"` -} - -type Config struct { - Modules []string `json:"modules,omitempty"` - Decks []Deck `json:"decks"` -} - -type DepracatedConfig struct { - Modules []string `json:"modules,omitempty"` - Pages []Page `json:"pages"` -} - -type Key struct { - Icon string `json:"icon,omitempty"` - SwitchPage int `json:"switch_page,omitempty"` - Text string `json:"text,omitempty"` - TextSize int `json:"text_size,omitempty"` - TextAlignment string `json:"text_alignment,omitempty"` - Keybind string `json:"keybind,omitempty"` - Command string `json:"command,omitempty"` - Brightness int `json:"brightness,omitempty"` - Url string `json:"url,omitempty"` - IconHandler string `json:"icon_handler,omitempty"` - KeyHandler string `json:"key_handler,omitempty"` - IconHandlerFields map[string]string `json:"icon_handler_fields,omitempty"` - KeyHandlerFields map[string]string `json:"key_handler_fields,omitempty"` - Buff image.Image `json:"-"` - IconHandlerStruct IconHandler `json:"-"` - KeyHandlerStruct KeyHandler `json:"-"` -} From 39653aed1bfdc24d93af275af677f4067ad214ef Mon Sep 17 00:00:00 2001 From: Louis Jones Date: Sat, 21 Mar 2026 21:43:08 +0000 Subject: [PATCH 4/6] Fix tests --- dbus_test.go | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/dbus_test.go b/dbus_test.go index f604861..20730a0 100644 --- a/dbus_test.go +++ b/dbus_test.go @@ -188,7 +188,7 @@ func TestGetModules(t *testing.T) { modules := []*Module{ { Name: "Example1", - IconFields: []Field{ + ForegroundFields: []Field{ { Title: "File", Name: "file", @@ -196,20 +196,16 @@ func TestGetModules(t *testing.T) { FileTypes: []string{"*.gif"}, }, }, - KeyFields: []Field{ + InputFields: []Field{ { Title: "Something", Name: "something", Type: Text, }, }, - LcdFields: nil, - KnobOrTouchFields: nil, - LinkedFields: nil, - IsIcon: true, - IsKey: true, - IsLcd: false, - IsKnob: false, + LinkedFields: nil, + IsForeground: true, + IsInput: true, }, } From 87082d6b8f7d270613d07299b6e4060fd9c1d1d4 Mon Sep 17 00:00:00 2001 From: Louis Jones Date: Sun, 22 Mar 2026 16:08:37 +0000 Subject: [PATCH 5/6] Update docs --- README.md | 78 ++++++++++++++++--------------------------------------- 1 file changed, 23 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index 4d3c747..59ed7b6 100644 --- a/README.md +++ b/README.md @@ -66,72 +66,40 @@ imgWithText, _ := api.DrawText(resizedImg, "Hello", 0, "CENTER") ### Implementing handlers ```go -// Implement a key handler -type MyKeyHandler struct{} - -func (h *MyKeyHandler) Key(key api.KeyConfigV3, info api.StreamDeckInfoV1) { - // Handle key press +// Implement a handler +type MyHandler struct{ + running bool } -// Implement an icon handler -type MyIconHandler struct { - running bool +func (h *MyHandler) Input(fields map[string]any, handlerType HandlerType, info StreamDeckInfoV1, event InputEvent) { + switch event.EventType { + case api.KNOB_CW: + // Handle clockwise rotation + case api.KNOB_CCW: + // Handle counter-clockwise rotation + case api.KNOB_PRESS: + // Handle knob press + case api.KEY_PRESS: + // Handle key press + } } -func (h *MyIconHandler) Start(key api.KeyConfigV3, info api.StreamDeckInfoV1, callback func(image image.Image)) { - h.running = true - // Generate and update icon - // Call callback with new images when needed +func (h *MyHandler) Start(fields map[string]any, handlerType HandlerType, info StreamDeckInfoV1, callback func(image image.Image)) { + // Generate image and send it back via callback } -func (h *MyIconHandler) IsRunning() bool { +func (h *MyHandler) IsRunning() bool { return h.running } -func (h *MyIconHandler) SetRunning(running bool) { +func (h *MyHandler) SetRunning(running bool) { h.running = running } -func (h *MyIconHandler) Stop() { +func (h *MyHandler) Stop() { h.running = false // Clean up resources and stop calling callback } - -// Implement an LCD handler (for Stream Deck Plus) -type MyLcdHandler struct { - running bool -} - -func (h *MyLcdHandler) Start(knob api.KnobConfigV3, info api.StreamDeckInfoV1, callback func(image image.Image)) { - h.running = true - // Generate and update LCD image -} - -func (h *MyLcdHandler) IsRunning() bool { - return h.running -} - -func (h *MyLcdHandler) SetRunning(running bool) { - h.running = running -} - -func (h *MyLcdHandler) Stop() { - h.running = false -} - -// Implement a knob or touch handler (for Stream Deck Plus) -type MyKnobHandler struct{} - -func (h *MyKnobHandler) Input(knob api.KnobConfigV3, info api.StreamDeckInfoV1, event api.InputEvent) { - switch event.EventType { - case api.KNOB_CW: - // Handle clockwise rotation - case api.KNOB_CCW: - // Handle counter-clockwise rotation - case api.KNOB_PRESS: - // Handle knob press - } -} ``` ## API Documentation @@ -139,10 +107,10 @@ func (h *MyKnobHandler) Input(knob api.KnobConfigV3, info api.StreamDeckInfoV1, The API provides several interfaces for handling Stream Deck interactions: - `Handler`: Base interface for all handlers -- `IconHandler`: For handling dynamic icons/images -- `KeyHandler`: For handling key press events -- `LcdHandler`: For handling LCD displays (Stream Deck Plus) -- `KnobOrTouchHandler`: For handling knob rotations and touch events (Stream Deck Plus) +- `ForegroundHandler`: For handling dynamic icons/images +- `InputHandler`: For handling input events +- `BackgroundHandler`: For handling dynamic backgrounds for individual displays, or whole deck +- `CombinedHandler`: For handlers that can do foregrounds and handle input, if this handler is applied to the foreground and input of a specific key/lcd segment, the same instance of the struct will be used, so resources can be shared Key components: From 787fa93070741540d5f9ea0158940ac870d1a967 Mon Sep 17 00:00:00 2001 From: Louis Jones Date: Sun, 22 Mar 2026 17:32:22 +0000 Subject: [PATCH 6/6] Add tests --- img_test.go | 268 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 268 insertions(+) diff --git a/img_test.go b/img_test.go index d7b4096..11c2d23 100644 --- a/img_test.go +++ b/img_test.go @@ -2,7 +2,9 @@ package api import ( "image" + "image/color" "image/draw" + "log" "math/rand" "testing" @@ -11,7 +13,18 @@ import ( "github.com/unix-streamdeck/api/v2/mocks/mock_api" "github.com/unix-streamdeck/gg" "go.uber.org/mock/gomock" + "golang.org/x/image/font/gofont/gobold" + "golang.org/x/image/font/gofont/gobolditalic" + "golang.org/x/image/font/gofont/goitalic" + "golang.org/x/image/font/gofont/gomedium" + "golang.org/x/image/font/gofont/gomediumitalic" + "golang.org/x/image/font/gofont/gomono" + "golang.org/x/image/font/gofont/gomonobold" + "golang.org/x/image/font/gofont/gomonobolditalic" + "golang.org/x/image/font/gofont/gomonoitalic" "golang.org/x/image/font/gofont/goregular" + "golang.org/x/image/font/gofont/gosmallcaps" + "golang.org/x/image/font/gofont/gosmallcapsitalic" ) func TestDrawText(t *testing.T) { @@ -143,6 +156,261 @@ func TestResizeImageWH(t *testing.T) { assertions.Equal(resizedImage.Bounds().Max.Y, newHeight) } +func TestDrawText_WithColor(t *testing.T) { + assertions := assert.New(t) + ctrl := gomock.NewController(t) + + context := mock_api.NewMockIContext(ctrl) + + context.EXPECT().SetRGB(1.0, 1.0, 1.0).Times(1) + context.EXPECT().SetHexColor("#FF0000").Times(1) + + context.EXPECT().Width().Return(72).Times(2) + context.EXPECT().Height().Return(72).Times(1) + + context.EXPECT().SetFontFace(gomock.Any()).Times(2) + + context.EXPECT().MeasureMultilineString("Test", 1.0).Return(20.0, 24.0).Times(1) + + context.EXPECT().WordWrap("Test", 62.0).Return([]string{"Test"}).Times(1) + + context.EXPECT().DrawStringWrapped("Test", 36.0, 36.0, 0.5, 0.5, 62.0, 1.0, gg.AlignCenter) + + mockImg := setupImage(72, 72) + + context.EXPECT().Image().Return(mockImg).Times(1) + + img, err := drawText(context, "Test", DrawTextOptions{Colour: "#FF0000"}) + + assertions.Equal(mockImg, img) + assertions.Nil(err) +} + +func TestDrawText_WithFontSize(t *testing.T) { + assertions := assert.New(t) + ctrl := gomock.NewController(t) + + context := mock_api.NewMockIContext(ctrl) + + context.EXPECT().SetRGB(1.0, 1.0, 1.0).Times(1) + + context.EXPECT().Width().Return(72).Times(2) + context.EXPECT().Height().Return(72).Times(1) + + context.EXPECT().SetFontFace(gomock.Any()).Times(2) + + context.EXPECT().MeasureMultilineString("Test", 1.0).Return(25.0, 12.0).Times(1) + + context.EXPECT().WordWrap("Test", 62.0).Return([]string{"Test"}).Times(1) + + context.EXPECT().DrawStringWrapped("Test", 36.0, 36.0, 0.5, 0.5, 62.0, 1.0, gg.AlignCenter) + + mockImg := setupImage(72, 72) + + context.EXPECT().Image().Return(mockImg).Times(1) + + img, err := drawText(context, "Test", DrawTextOptions{FontSize: 16}) + + assertions.Equal(mockImg, img) + assertions.Nil(err) +} + +func TestDrawText_WithNewlines(t *testing.T) { + assertions := assert.New(t) + ctrl := gomock.NewController(t) + + context := mock_api.NewMockIContext(ctrl) + + context.EXPECT().SetRGB(1.0, 1.0, 1.0).Times(1) + + context.EXPECT().Width().Return(72).Times(2) + context.EXPECT().Height().Return(72).Times(1) + + context.EXPECT().SetFontFace(gomock.Any()).Times(2) + + context.EXPECT().MeasureMultilineString("Line1\nLine2", 1.0).Return(30.0, 48.0).Times(1) + + context.EXPECT().WordWrap("Line1\nLine2", 62.0).Return([]string{"Line1", "Line2"}).Times(1) + + context.EXPECT().DrawStringWrapped("Line1\nLine2", 36.0, 36.0, 0.5, 0.5, 62.0, 1.0, gg.AlignCenter) + + mockImg := setupImage(72, 72) + + context.EXPECT().Image().Return(mockImg).Times(1) + + img, err := drawText(context, "Line1\nLine2", DrawTextOptions{}) + + assertions.Equal(mockImg, img) + assertions.Nil(err) +} + +func Test_loadFontFace_Bold(t *testing.T) { + assertions := assert.New(t) + assertions.Equal(gobold.TTF, loadFontFace("bold")) +} + +func Test_loadFontFace_BoldItalic(t *testing.T) { + assertions := assert.New(t) + assertions.Equal(gobolditalic.TTF, loadFontFace("bolditalic")) +} + +func Test_loadFontFace_Italic(t *testing.T) { + assertions := assert.New(t) + assertions.Equal(goitalic.TTF, loadFontFace("italic")) +} + +func Test_loadFontFace_Medium(t *testing.T) { + assertions := assert.New(t) + assertions.Equal(gomedium.TTF, loadFontFace("medium")) +} + +func Test_loadFontFace_MediumItalic(t *testing.T) { + assertions := assert.New(t) + assertions.Equal(gomediumitalic.TTF, loadFontFace("mediumitalic")) +} + +func Test_loadFontFace_Mono(t *testing.T) { + assertions := assert.New(t) + assertions.Equal(gomono.TTF, loadFontFace("mono")) +} + +func Test_loadFontFace_MonoBold(t *testing.T) { + assertions := assert.New(t) + assertions.Equal(gomonobold.TTF, loadFontFace("monobold")) +} + +func Test_loadFontFace_MonoBoldItalic(t *testing.T) { + assertions := assert.New(t) + assertions.Equal(gomonobolditalic.TTF, loadFontFace("monobolditalic")) +} + +func Test_loadFontFace_MonoItalic(t *testing.T) { + assertions := assert.New(t) + assertions.Equal(gomonoitalic.TTF, loadFontFace("monoitalic")) +} + +func Test_loadFontFace_SmallCaps(t *testing.T) { + assertions := assert.New(t) + assertions.Equal(gosmallcaps.TTF, loadFontFace("smallcaps")) +} + +func Test_loadFontFace_SmallCapsItalic(t *testing.T) { + assertions := assert.New(t) + assertions.Equal(gosmallcapsitalic.TTF, loadFontFace("smallcapsitalic")) +} + +func Test_loadFontFace_Regular(t *testing.T) { + assertions := assert.New(t) + assertions.Equal(goregular.TTF, loadFontFace("regular")) +} + +func Test_loadFontFace_Default(t *testing.T) { + assertions := assert.New(t) + assertions.Equal(goregular.TTF, loadFontFace("unknown")) +} + +func TestHexColor(t *testing.T) { + assertions := assert.New(t) + result := HexColor("#FF0000") + expected := color.RGBA{R: 255, G: 0, B: 0, A: 255} + assertions.Equal(expected, result) +} + +func TestHexColor_Green(t *testing.T) { + assertions := assert.New(t) + result := HexColor("#00FF00") + expected := color.RGBA{R: 0, G: 255, B: 0, A: 255} + assertions.Equal(expected, result) +} + +func TestHexColor_Blue(t *testing.T) { + assertions := assert.New(t) + result := HexColor("#0000FF") + expected := color.RGBA{R: 0, G: 0, B: 255, A: 255} + assertions.Equal(expected, result) +} + +func TestLayerImages_Success(t *testing.T) { + assertions := assert.New(t) + img1 := setupImage(72, 72) + draw.Draw(img1, img1.Bounds(), &image.Uniform{color.RGBA{255, 0, 0, 255}}, image.Point{}, draw.Src) + + img2 := image.NewRGBA(image.Rect(0, 0, 72, 72)) + draw.Draw(img2, img2.Bounds(), &image.Uniform{color.RGBA{0, 255, 0, 128}}, image.Point{}, draw.Src) + + result, err := LayerImages(72, 72, img1, img2) + + log.Println() + + assertions.NotNil(result) + assertions.Nil(err) + assertions.Equal(72, result.Bounds().Dx()) + assertions.Equal(72, result.Bounds().Dy()) + + assertions.Equal(color.RGBA{127, 255, 0, 255}, result.At(0, 0)) + + rgba, ok := result.(*image.RGBA) + assertions.True(ok) + assertions.NotNil(rgba) +} + +func TestLayerImages_NoImages(t *testing.T) { + assertions := assert.New(t) + + result, err := LayerImages(72, 72) + + assertions.Nil(result) + assertions.NotNil(err) + assertions.Equal("no images supplied", err.Error()) +} + +func TestLayerImages_NilImages(t *testing.T) { + assertions := assert.New(t) + + result, err := LayerImages(72, 72, nil, nil) + + assertions.Nil(result) + assertions.NotNil(err) + assertions.Equal("no valid images supplied", err.Error()) +} + +func TestLayerImages_WrongSize(t *testing.T) { + assertions := assert.New(t) + img1 := setupImage(50, 50) + img2 := setupImage(60, 60) + + result, err := LayerImages(72, 72, img1, img2) + + assertions.Nil(result) + assertions.NotNil(err) + assertions.Equal("no valid images supplied", err.Error()) +} + +func TestLayerImages_MixedValidInvalid(t *testing.T) { + assertions := assert.New(t) + img1 := setupImage(72, 72) + img2 := setupImage(50, 50) + + result, err := LayerImages(72, 72, img1, img2) + + assertions.NotNil(result) + assertions.Nil(err) +} + +func TestSubImage(t *testing.T) { + assertions := assert.New(t) + img := setupImage(100, 100) + + result := SubImage(img, 10, 10, 50, 50) + + assertions.NotNil(result) + assertions.Equal(40, result.Bounds().Dx()) + assertions.Equal(40, result.Bounds().Dy()) + assertions.Equal(10, result.Bounds().Min.X) + assertions.Equal(10, result.Bounds().Min.Y) + assertions.Equal(50, result.Bounds().Max.X) + assertions.Equal(50, result.Bounds().Max.Y) +} func setupImage(width int, height int) *image.RGBA { img := image.NewRGBA(image.Rect(0, 0, width, height)) draw.Draw(img, img.Bounds(), image.Black, image.ZP, draw.Src)