; _______________________________________________________________________________________
;|                                                                                       |
;| ..::FreshLib::..  Free, open source. Licensed under "BSD 2-clause" license."          |
;|_______________________________________________________________________________________|
;
;  Description: TButton object class
;
;  Target OS: Any
;
;  Dependencies:
;
;  Notes: Represents GUI button.
;_________________________________________________________________________________________
module "TButton library"


; Button states for the field TButton._state

bsNeutral    = 0
bsHovered    = 1
bsPressed    = 2

bsDisabled   = 3

; icon positions

iposLeft   = 0
iposRight  = 1
iposTop    = 2
iposBottom = 3
iposCenter = 4


object TButton, TWindow

; private fields

  ._state      dd ?      ; internal state variable.

  ._textalign dd ?
  ._icon      dd ?
  ._iconpos   dd ?

  ._OnClick   dd ?

  ._ModalResult dd ?

; parameters

  param .OnClick, ._OnClick, ._OnClick

  param .TextAlign,    ._textalign,  .SetTextAlign
  param .Icon,         ._icon,       .SetIcon
  param .IconPosition, ._iconpos,    .SetIconPos

  param .ModalResult, ._ModalResult, ._ModalResult

; methods

  ; parameters handling methods

  method .SetTextAlign, .value
  method .SetIcon, .value
  method .SetIconPos, .value

  ; system events methods

  method .Create, .Parent

  method .Paint

  method .EventMouseEnter
  method .EventMouseLeave

  method .EventButtonPress, .button, .kbdState, .x, .y
  method .EventButtonRelease, .button, .kbdState, .x, .y

  method .EventKeyPress, .utf8, .scancode, .kbdState
  method .EventKeyRelease, .scancode, .kbdState

endobj


interface TButton.OnClick, .self, .button




; new


method TButton.Create
begin
        inherited [.Parent]

        mov     eax, [.self]
        mov     [eax+TButton._cursor], mcHand
        mov     [eax+TButton.__want_focus], TRUE

        return
endp





method TButton.Paint

 .bounds TBounds
 .clTxt dd ?
 .canvas dd ?

 .caption dd ?
 .enabled dd ?
 .icon    dd ?

begin
        pushad

        mov     esi, [.self]

        get     edx, esi, TButton:Action
        test    edx, edx
        jz      .get_normal

        get     [.caption], edx, TAction:Caption
        get     [.icon],    edx, TAction:Icon
        jmp     .get_enabled

.get_normal:

        get     [.caption], esi, TButton:Caption
        get     [.icon], esi, TButton:Icon

.get_enabled:
        get     [.enabled], esi, TButton:Enabled        ; it will return the TAction:Enabled if attached.

.paint_it:
        mov     eax, [esi+TWindow._canvas]
        test    eax, eax
        jz      .finish

        mov     [.canvas], eax

        mov     eax, [esi+TButton._state]
        cmp     [.enabled], 0
        jne     .state_ok

        mov     eax, bsDisabled

.state_ok:
        mov     edi, [GUI.clBtnBk+4*eax]           ; background color
        mov     ecx, [GUI.clBtnTxt+4*eax]          ; text color
        mov     edx, [GUI.btnBorder+4*eax]
        cmp     [.enabled], 0
        jne     .border_ok

        or      edx, bxDisabled

.border_ok:
        mov     [.clTxt], ecx

        mov     ecx, [esi+TButton._width]
        mov     eax, [esi+TButton._height]

        mov     [.bounds.x], 0
        mov     [.bounds.y], 0
        mov     [.bounds.width], ecx
        mov     [.bounds.height], eax
        lea     eax, [.bounds]



        stdcall [DrawBox], [.canvas], eax, edi, edx, [GUI.boxBorderWidth]

; first draw the icon and then resize bounds for the text.

        mov     ebx, [GUI.btnMarginX]
        mov     edx, [GUI.btnMarginY]

        add     [.bounds.x], ebx
        add     [.bounds.y], edx
        sub     [.bounds.width], ebx
        sub     [.bounds.height], edx
        sub     [.bounds.width], ebx
        sub     [.bounds.height], edx


        mov     edi, [.icon]
        test    edi, edi
        jz      .draw_text

        cmp     [esi+TButton._iconpos], iposCenter
        jne     @f

        mov     eax, [.bounds.width]
        sub     eax, [edi+TImage.width]
        sar     eax, 1
        add     eax, [.bounds.x]

        mov     ecx, [.bounds.height]
        sub     ecx, [edi+TImage.height]
        sar     ecx, 1
        add     ecx, [.bounds.y]                ; x coordinate of the icon.

        jmp     .draw_icon

@@:
        cmp     [esi+TButton._iconpos], iposLeft
        jne     @f

; align left
        mov     eax, [.bounds.x]

        mov     ecx, [.bounds.height]
        sub     ecx, [edi+TImage.height]
        sar     ecx, 1
        add     ecx, [.bounds.y]                ; x coordinate of the icon.

        add     ebx, [edi+TImage.width]
        add     [.bounds.x], ebx
        sub     [.bounds.width], ebx
        jmp     .draw_icon

@@:
        cmp     [esi+TButton._iconpos], iposRight
        jne     @f

; align right
        mov     eax, [.bounds.x]
        add     eax, [.bounds.width]
        sub     eax, [edi+TImage.width]

        add     ebx, [edi+TImage.width]
        sub     [.bounds.width], ebx

        mov     ecx, [.bounds.height]
        sub     ecx, [edi+TImage.height]
        sar     ecx, 1
        add     ecx, [.bounds.y]
        jmp     .draw_icon

@@:
        cmp     [esi+TButton._iconpos], iposTop
        jne     @f

; align top
        mov     eax, [.bounds.width]
        sub     eax, [edi+TImage.width]
        sar     eax, 1
        add     eax, [.bounds.x]

        mov     ecx, [.bounds.y]

        add     edx, [edi+TImage.height]

        add     [.bounds.y], edx
        sub     [.bounds.height], edx
        jmp     .draw_icon

@@:
        cmp     [esi+TButton._iconpos], iposBottom
        jne     .draw_text

; align bottom
        mov     ecx, [.bounds.y]
        add     ecx, [.bounds.height]
        sub     ecx, [edi+TImage.height]

        add     edx, [edi+TImage.height]
        sub     [.bounds.height], edx

        mov     eax, [.bounds.width]
        sub     eax, [edi+TImage.width]
        sar     eax, 1
        add     eax, [.bounds.x]

.draw_icon:
        cmp     [esi+TButton._state], bsPressed
        jne     .blend_icon

        add     eax, [GUI.btnPressedOfsX]
        add     ecx, [GUI.btnPressedOfsY]

.blend_icon:
        stdcall BlendImage, [.canvas], eax, ecx, edi, 0, 0, [edi+TImage.width], [edi+TImage.height]

.draw_text:
        get     edi, esi, TButton:TextAlign

        mov     eax, [.caption]
        test    eax, eax
        jz      .textok

        cmp     [esi+TButton._state], bsPressed
        jne     .textofsok

        mov     ecx, [GUI.btnPressedOfsX]
        add     [.bounds.x], ecx
        mov     ecx, [GUI.btnPressedOfsY]
        add     [.bounds.y], ecx

.textofsok:
        stdcall DrawTextBox, [.canvas], eax, [.bounds.x], [.bounds.y], [.bounds.width], [.bounds.height], 2, edi, [GUI.DefaultFont], [.clTxt]
        stdcall StrDel, eax


.textok:
        cmp     esi, [__FocusedWindow]
        jne     .not_focused

        mov     eax, [esi+TButton._height]
        mov     ecx, [esi+TButton._width]
        sub     eax, 2
        sub     ecx, 4
        stdcall DrawSolidRect, [.canvas], 0, eax, [esi+TButton._width], 2, [GUI.clBorderFocused]

.not_focused:

.finish:
        inherited

        popad
        return
endp




method TButton.EventMouseEnter
begin
        mov     eax, [.self]
        mov     [eax+TButton._state], bsHovered
        exec    eax, TButton:Refresh

        inherited
        return
endp




method TButton.EventMouseLeave
begin
        mov     eax, [.self]

        mov     [eax+TButton._state], bsNeutral
        exec    eax, TButton:Refresh

        inherited
        return
endp



method TButton.EventButtonPress
begin
        mov     eax, [.self]

        mov     [eax+TButton._state], bsPressed
        exec    eax, TButton:Refresh
        return
endp



method TButton.EventButtonRelease
begin
        pushad

        mov     esi, [.self]

        cmp     [esi+TButton._state], bsPressed
        jne     .finish

        mov     [esi+TButton._state], bsHovered
        exec    esi, TButton:Refresh

        cmp     [esi+TButton._ModalResult], mrNone
        je      @f

        get     eax, esi, TButton:Parent

        istype  eax, TForm
        jne     @f

        set     eax, TForm:ModalResult, [esi+TButton._ModalResult]

@@:
        get     eax, esi, TButton:OnClick
        test    eax, eax
        jz      .click_ok

        stdcall eax, esi, [.button]       ; OnClick event

.click_ok:
        get     eax, esi, TButton:Action
        test    eax, eax
        jz      .finish

        exec    eax, TAction:Execute, [.self]

.finish:
        popad
        return
endp




method TButton.EventKeyPress
begin
        push    ecx

        cmp     [.utf8], ' '
        jne     .finish

        mov     ecx, [.self]
        mov     [ecx+TButton._state], bsPressed

        exec    ecx, TWindow:EventButtonRelease, mbLeft, [.kbdState], 0, 0

        mov     [ecx+TButton._state], bsPressed
        exec    ecx, TWindow:Refresh

.finish:
        inherited [.utf8], [.scancode], [.kbdState]
        pop     ecx
        return
endp



method TButton.EventKeyRelease
begin
        push    ecx
        mov     ecx, [.self]
        mov     [ecx+TButton._state], bsNeutral

        exec    ecx, TWindow:Refresh
        pop     ecx

        return
endp



method TButton.SetTextAlign
begin
        push    ecx

        mov     ecx, [.self]
        push    [.value]
        pop     [ecx+TButton._textalign]

        exec    ecx, TWindow:Refresh
        pop     ecx
        return
endp



method TButton.SetIcon
begin
        push    ecx

        mov     ecx, [.self]
        push    [.value]
        pop     [ecx+TButton._icon]

        exec    ecx, TWindow:Refresh
        pop     ecx
        return
endp


method TButton.SetIconPos
begin
        push    ecx

        mov     ecx, [.self]
        push    [.value]
        pop     [ecx+TButton._iconpos]

        exec    ecx, TWindow:Refresh
        pop     ecx
        return
endp



endmodule