메뉴 여닫기
개인 메뉴 토글
로그인하지 않음
만약 지금 편집한다면 당신의 IP 주소가 공개될 수 있습니다.

버튼 스타일

데브카페
Devcafe (토론 | 기여)님의 2026년 5월 15일 (금) 17:59 판
(차이) ← 이전 판 | 최신판 (차이) | 다음 판 → (차이)

플랫 버튼 스타일

    1. 1. BS_FLAT 스타일 (가장 간단)

#include <GUIConstantsEx.au3>
#include <ButtonConstants.au3>

$hGUI = GUICreate("Flat Button", 300, 200)
GUISetBkColor(0xFFFFFF)

$hBtn = GUICtrlCreateButton("클릭", 50, 50, 200, 40, $BS_FLAT)

GUISetState()
While GUIGetMsg() <> $GUI_EVENT_CLOSE
WEnd


3D 테두리가 제거되지만, 마우스 오버 시 테두리가 살아나서 완전한 플랫은 아닙니다.

    1. 2. Label + 색상 조합 (완전 플랫)

#include <GUIConstantsEx.au3>
#include <StaticConstants.au3>

$hGUI = GUICreate("Flat Button", 400, 300)
GUISetBkColor(0xFFFFFF)

; 버튼처럼 동작하는 Label
$hBtn1 = _CREATE_FLAT_BUTTON("저장",    30,  50, 150, 45, 0x2196F3, 0xFFFFFF)
$hBtn2 = _CREATE_FLAT_BUTTON("취소",   210,  50, 150, 45, 0xF44336, 0xFFFFFF)
$hBtn3 = _CREATE_FLAT_BUTTON("설정",    30, 120, 150, 45, 0x4CAF50, 0xFFFFFF)
$hBtn4 = _CREATE_FLAT_BUTTON("닫기",   210, 120, 150, 45, 0x757575, 0xFFFFFF)

GUISetState()

While 1
    Switch GUIGetMsg()
        Case $GUI_EVENT_CLOSE
            Exit
        Case $hBtn1
            MsgBox(0, "", "저장 클릭")
        Case $hBtn2
            MsgBox(0, "", "취소 클릭")
        Case $hBtn3
            MsgBox(0, "", "설정 클릭")
        Case $hBtn4
            MsgBox(0, "", "닫기 클릭")
    EndSwitch
WEnd

Func _CREATE_FLAT_BUTTON($sText, $iX, $iY, $iW, $iH, $iBgColor, $iFontColor)
    Local $hLabel = GUICtrlCreateLabel($sText, $iX, $iY, $iW, $iH, _
            BitOR($SS_CENTER, $SS_CENTERIMAGE))
    GUICtrlSetBkColor($hLabel, $iBgColor)
    GUICtrlSetColor($hLabel, $iFontColor)
    GUICtrlSetFont($hLabel, 11, 600, 0, "맑은 고딕")
    GUICtrlSetCursor($hLabel, 0)  ; 손가락 커서
    Return $hLabel
EndFunc
    1. 3. GDI+ Owner-Draw (호버 효과 포함)

#include <GUIConstantsEx.au3>
#include <GDIPlus.au3>
#include <WindowsConstants.au3>
#include <WinAPI.au3>
#include <StaticConstants.au3>

Global Const $FLAT_COLOR_NORMAL  = 0xFF2196F3  ; 파란색
Global Const $FLAT_COLOR_HOVER   = 0xFF1976D2  ; 진한 파란색
Global Const $FLAT_COLOR_PRESS   = 0xFF0D47A1  ; 더 진한 파란색
Global Const $FLAT_COLOR_TEXT    = 0xFFFFFFFF  ; 흰색

Global $g_aBtns[0][6]  ; [n][CtrlID, X, Y, W, H, Text]
Global $g_iHoverIdx = -1
Global $g_iPressIdx = -1
Global $g_hGUI

_GDIPlus_Startup()

$g_hGUI = GUICreate("GDI+ Flat Buttons", 400, 300)
GUISetBkColor(0xF5F5F5)

; GDI+ 그리기용 Pic 컨트롤 (전체 영역)
Global $g_hPic = GUICtrlCreatePic("", 0, 0, 400, 300)
GUICtrlSetState($g_hPic, $GUI_DISABLE)

; 버튼 등록
_ADD_FLAT_BUTTON(30,   50, 150, 45, "저장")
_ADD_FLAT_BUTTON(210,  50, 150, 45, "취소")
_ADD_FLAT_BUTTON(30,  120, 150, 45, "설정")
_ADD_FLAT_BUTTON(210, 120, 150, 45, "닫기")

_DRAW_ALL_BUTTONS()

GUISetState()
GUIRegisterMsg($WM_MOUSEMOVE, "_WM_MOUSEMOVE")
GUIRegisterMsg($WM_LBUTTONDOWN, "_WM_LBUTTONDOWN")
GUIRegisterMsg($WM_LBUTTONUP, "_WM_LBUTTONUP")

While GUIGetMsg() <> $GUI_EVENT_CLOSE
WEnd

_GDIPlus_Shutdown()

; ── 버튼 추가 ──
Func _ADD_FLAT_BUTTON($iX, $iY, $iW, $iH, $sText)
    Local $iIdx = UBound($g_aBtns)
    ReDim $g_aBtns[$iIdx + 1][6]
    $g_aBtns[$iIdx][0] = $iIdx
    $g_aBtns[$iIdx][1] = $iX
    $g_aBtns[$iIdx][2] = $iY
    $g_aBtns[$iIdx][3] = $iW
    $g_aBtns[$iIdx][4] = $iH
    $g_aBtns[$iIdx][5] = $sText
EndFunc

; ── 전체 버튼 그리기 ──
Func _DRAW_ALL_BUTTONS()
    Local $hBitmap = _GDIPlus_BitmapCreateFromScan0(400, 300)
    Local $hGfx = _GDIPlus_ImageGetGraphicsContext($hBitmap)
    _GDIPlus_GraphicsSetSmoothingMode($hGfx, 4)
    _GDIPlus_GraphicsSetTextRenderingHint($hGfx, 5)
    _GDIPlus_GraphicsClear($hGfx, 0xFFF5F5F5)

    Local $hFontFamily = _GDIPlus_FontFamilyCreate("맑은 고딕")
    Local $hFont = _GDIPlus_FontCreate($hFontFamily, 12, 1)
    Local $hFormat = _GDIPlus_StringFormatCreate()
    _GDIPlus_StringFormatSetAlign($hFormat, 1)      ; 가로 중앙
    _GDIPlus_StringFormatSetLineAlign($hFormat, 1)   ; 세로 중앙

    Local $hTextBrush = _GDIPlus_BrushCreateSolid($FLAT_COLOR_TEXT)

    For $i = 0 To UBound($g_aBtns) - 1
        ; 상태별 색상 결정
        Local $iColor = $FLAT_COLOR_NORMAL
        If $i = $g_iPressIdx Then
            $iColor = $FLAT_COLOR_PRESS
        ElseIf $i = $g_iHoverIdx Then
            $iColor = $FLAT_COLOR_HOVER
        EndIf

        ; 둥근 사각형 배경
        Local $hPath = _GDIPlus_PathCreate()
        Local $r = 8  ; 둥근 반지름
        Local $bX = $g_aBtns[$i][1], $bY = $g_aBtns[$i][2]
        Local $bW = $g_aBtns[$i][3], $bH = $g_aBtns[$i][4]
        _GDIPlus_PathAddArc($hPath, $bX, $bY, $r * 2, $r * 2, 180, 90)
        _GDIPlus_PathAddArc($hPath, $bX + $bW - $r * 2, $bY, $r * 2, $r * 2, 270, 90)
        _GDIPlus_PathAddArc($hPath, $bX + $bW - $r * 2, $bY + $bH - $r * 2, $r * 2, $r * 2, 0, 90)
        _GDIPlus_PathAddArc($hPath, $bX, $bY + $bH - $r * 2, $r * 2, $r * 2, 90, 90)
        _GDIPlus_PathCloseFigure($hPath)

        Local $hBrush = _GDIPlus_BrushCreateSolid($iColor)
        _GDIPlus_GraphicsFillPath($hGfx, $hPath, $hBrush)
        _GDIPlus_BrushDispose($hBrush)
        _GDIPlus_PathDispose($hPath)

        ; 텍스트
        Local $tRect = _GDIPlus_RectFCreate($bX, $bY, $bW, $bH)
        _GDIPlus_GraphicsDrawStringEx($hGfx, $g_aBtns[$i][5], $hFont, $tRect, $hFormat, $hTextBrush)
    Next

    ; Pic에 적용
    Local $hHBmp = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hBitmap)
    _WinAPI_DeleteObject(GUICtrlSendMsg($g_hPic, 0x0172, 0, $hHBmp))  ; STM_SETIMAGE
    _WinAPI_DeleteObject($hHBmp)

    _GDIPlus_BrushDispose($hTextBrush)
    _GDIPlus_FontDispose($hFont)
    _GDIPlus_FontFamilyDispose($hFontFamily)
    _GDIPlus_StringFormatDispose($hFormat)
    _GDIPlus_GraphicsDispose($hGfx)
    _GDIPlus_BitmapDispose($hBitmap)
EndFunc

; ── 마우스 위치로 버튼 인덱스 찾기 ──
Func _HIT_TEST($iMX, $iMY)
    For $i = 0 To UBound($g_aBtns) - 1
        If $iMX >= $g_aBtns[$i][1] And $iMX <= $g_aBtns[$i][1] + $g_aBtns[$i][3] And _
           $iMY >= $g_aBtns[$i][2] And $iMY <= $g_aBtns[$i][2] + $g_aBtns[$i][4] Then
            Return $i
        EndIf
    Next
    Return -1
EndFunc

; ── 마우스 이동 ──
Func _WM_MOUSEMOVE($hWnd, $iMsg, $wParam, $lParam)
    Local $iMX = BitAND($lParam, 0xFFFF)
    Local $iMY = BitShift(BitAND($lParam, 0xFFFF0000), 16)
    Local $iIdx = _HIT_TEST($iMX, $iMY)
    If $iIdx <> $g_iHoverIdx Then
        $g_iHoverIdx = $iIdx
        _DRAW_ALL_BUTTONS()
    EndIf
    Return $GUI_RUNDEFMSG
EndFunc

; ── 마우스 누름 ──
Func _WM_LBUTTONDOWN($hWnd, $iMsg, $wParam, $lParam)
    Local $iMX = BitAND($lParam, 0xFFFF)
    Local $iMY = BitShift(BitAND($lParam, 0xFFFF0000), 16)
    $g_iPressIdx = _HIT_TEST($iMX, $iMY)
    If $g_iPressIdx >= 0 Then _DRAW_ALL_BUTTONS()
    Return $GUI_RUNDEFMSG
EndFunc

; ── 마우스 뗌 ──
Func _WM_LBUTTONUP($hWnd, $iMsg, $wParam, $lParam)
    Local $iMX = BitAND($lParam, 0xFFFF)
    Local $iMY = BitShift(BitAND($lParam, 0xFFFF0000), 16)
    Local $iIdx = _HIT_TEST($iMX, $iMY)
    If $iIdx >= 0 And $iIdx = $g_iPressIdx Then
        ; 버튼 클릭 이벤트
        MsgBox(0, "클릭", $g_aBtns[$iIdx][5] & " 버튼 클릭!")
    EndIf
    $g_iPressIdx = -1
    _DRAW_ALL_BUTTONS()
    Return $GUI_RUNDEFMSG
EndFunc


세 방식의 차이를 정리하면 다음과 같습니다.

    • 방법 1 (BS_FLAT)** — 코드 한 줄이면 되지만 Windows 기본 버튼 렌더링에 의존하므로 완전한 플랫이 아닙니다. 마우스 오버 시 3D 테두리가 나타납니다.
    • 방법 2 (Label 활용)** — Label에 배경색과 폰트색을 입히는 방식으로, 코드가 간결하면서도 완전 플랫합니다. 다만 호버/프레스 시 색상 변화를 주려면 타이머나 메시지 처리를 추가해야 합니다.
    • 방법 3 (GDI+ Owner-Draw)** — 가장 자유도가 높습니다. 둥근 모서리, 호버 색상 변화, 프레스 효과까지 완전히 커스텀 가능합니다. 코드량이 많지만 SQL*KEY처럼 UI를 세밀하게 제어해야 하는 프로그램에 적합합니다.


플랫한 버튼 스타일 예제


#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <GDIPlus.au3>
#include <WinAPI.au3>

; ── Tab Control 스타일 상수 ──
Global Const $TCS_MULTILINE      = 0x0200
Global Const $TCS_BUTTONS        = 0x0100
Global Const $TCS_FLATBUTTONS    = 0x0008
Global Const $TCS_OWNERDRAWFIXED = 0x2000
Global Const $ODT_TAB            = 101
Global Const $ODS_SELECTED       = 0x0001

; ── 색상 설정 ──
Global Const $CLR_TAB_SELECTED_BG   = 0x2196F3  ; 선택된 탭 배경 (파란색)
Global Const $CLR_TAB_SELECTED_TEXT  = 0xFFFFFF  ; 선택된 탭 텍스트 (흰색)
Global Const $CLR_TAB_NORMAL_BG     = 0xE0E0E0  ; 일반 탭 배경 (밝은 회색)
Global Const $CLR_TAB_NORMAL_TEXT   = 0x333333  ; 일반 탭 텍스트 (진한 회색)
Global Const $CLR_TAB_HOVER_BG      = 0xBBDEFB  ; 호버 탭 배경 (연한 파란색)
Global Const $CLR_TAB_HOVER_TEXT    = 0x1565C0  ; 호버 탭 텍스트
Global Const $CLR_GUI_BG            = 0xFAFAFA  ; GUI 배경
Global Const $CLR_CONTENT_BG        = 0xFFFFFF  ; 탭 콘텐츠 영역 배경

; ── 폰트 설정 ──
Global Const $TAB_FONT_NAME = "맑은 고딕"
Global Const $TAB_FONT_SIZE = 10

; ── 탭 데이터 ──
Global $g_aTabNames[] = [ _
    "환경설정", "단축키 관리", "실행파일", "후킹설정", _
    "Direct Key", "매크로", "히스토리", "도움말" _
]

Global $g_hGUI, $g_hTab
Global $g_aTabItems[UBound($g_aTabNames)]
Global $g_aTabLabels[UBound($g_aTabNames)]  ; 탭별 콘텐츠 라벨
Global $g_iHoverTab = -1

; ── GUI 생성 ──
$g_hGUI = GUICreate("SQL*KEY - Flat Tab Demo", 700, 500)
GUISetBkColor($CLR_GUI_BG)
GUISetFont($TAB_FONT_SIZE, 400, 0, $TAB_FONT_NAME)

; ── Tab 컨트롤 생성 ──
; TCS_OWNERDRAWFIXED: 직접 그리기
; TCS_BUTTONS: 버튼 스타일
; TCS_MULTILINE: 다중 행
; TCS_FLATBUTTONS: 플랫 버튼 (owner-draw와 함께 사용)
$g_hTab = GUICtrlCreateTab(10, 10, 680, 480, _
        BitOR($TCS_OWNERDRAWFIXED, $TCS_BUTTONS, $TCS_MULTILINE, $TCS_FLATBUTTONS))
GUICtrlSetFont($g_hTab, $TAB_FONT_SIZE, 400, 0, $TAB_FONT_NAME)

; ── TabItem 생성 ──
For $i = 0 To UBound($g_aTabNames) - 1
    $g_aTabItems[$i] = GUICtrlCreateTabItem($g_aTabNames[$i])

    ; 각 탭의 콘텐츠 예시
    $g_aTabLabels[$i] = GUICtrlCreateLabel( _
            "[ " & $g_aTabNames[$i] & " ] 탭 콘텐츠 영역", _
            30, 80, 640, 380)
    GUICtrlSetFont($g_aTabLabels[$i], 14, 400, 0, $TAB_FONT_NAME)
    GUICtrlSetColor($g_aTabLabels[$i], 0x757575)
    GUICtrlSetBkColor($g_aTabLabels[$i], $CLR_CONTENT_BG)
Next
GUICtrlCreateTabItem("")  ; 탭 아이템 종료

; ── WM_DRAWITEM 등록 (탭 커스텀 그리기) ──
GUIRegisterMsg($WM_DRAWITEM, "_WM_DRAWITEM")

; ── WM_MOUSEMOVE 등록 (호버 효과) ──
GUIRegisterMsg($WM_NOTIFY, "_WM_NOTIFY")

GUISetState(@SW_SHOW)

; ── 메인 루프 ──
While 1
    Switch GUIGetMsg()
        Case $GUI_EVENT_CLOSE
            Exit
    EndSwitch
WEnd

; ══════════════════════════════════════════════════════
; WM_DRAWITEM 핸들러 - 탭을 플랫 스타일로 직접 그리기
; ══════════════════════════════════════════════════════
Func _WM_DRAWITEM($hWnd, $iMsg, $wParam, $lParam)
    ; DRAWITEMSTRUCT 구조체 파싱
    Local $tDRAWITEM = DllStructCreate( _
            "uint CtlType;" & _
            "uint CtlID;" & _
            "uint itemID;" & _
            "uint itemAction;" & _
            "uint itemState;" & _
            "hwnd hwndItem;" & _
            "handle hDC;" & _
            "long rcLeft;long rcTop;long rcRight;long rcBottom;" & _
            "ulong_ptr itemData", _
            $lParam)

    Local $iCtlType  = DllStructGetData($tDRAWITEM, "CtlType")
    If $iCtlType <> $ODT_TAB Then Return $GUI_RUNDEFMSG

    Local $iItemID   = DllStructGetData($tDRAWITEM, "itemID")
    Local $iState    = DllStructGetData($tDRAWITEM, "itemState")
    Local $hDC       = DllStructGetData($tDRAWITEM, "hDC")
    Local $iLeft     = DllStructGetData($tDRAWITEM, "rcLeft")
    Local $iTop      = DllStructGetData($tDRAWITEM, "rcTop")
    Local $iRight    = DllStructGetData($tDRAWITEM, "rcRight")
    Local $iBottom   = DllStructGetData($tDRAWITEM, "rcBottom")

    Local $bSelected = BitAND($iState, $ODS_SELECTED)

    ; ── 색상 결정 ──
    Local $iBgColor, $iTextColor
    If $bSelected Then
        $iBgColor   = $CLR_TAB_SELECTED_BG
        $iTextColor = $CLR_TAB_SELECTED_TEXT
    ElseIf $iItemID = $g_iHoverTab Then
        $iBgColor   = $CLR_TAB_HOVER_BG
        $iTextColor = $CLR_TAB_HOVER_TEXT
    Else
        $iBgColor   = $CLR_TAB_NORMAL_BG
        $iTextColor = $CLR_TAB_NORMAL_TEXT
    EndIf

    ; ── 배경 채우기 (1px 간격으로 버튼 분리) ──
    Local $tRect = DllStructCreate("long Left;long Top;long Right;long Bottom")
    DllStructSetData($tRect, "Left",   $iLeft + 1)
    DllStructSetData($tRect, "Top",    $iTop + 1)
    DllStructSetData($tRect, "Right",  $iRight - 1)
    DllStructSetData($tRect, "Bottom", $iBottom - 1)

    Local $hBrush = _WinAPI_CreateSolidBrush($iBgColor)
    _WinAPI_FillRect($hDC, $tRect, $hBrush)
    _WinAPI_DeleteObject($hBrush)

    ; ── 선택된 탭 하단 액센트 라인 ──
    If $bSelected Then
        Local $tAccent = DllStructCreate("long Left;long Top;long Right;long Bottom")
        DllStructSetData($tAccent, "Left",   $iLeft + 1)
        DllStructSetData($tAccent, "Top",    $iBottom - 4)
        DllStructSetData($tAccent, "Right",  $iRight - 1)
        DllStructSetData($tAccent, "Bottom", $iBottom - 1)
        Local $hAccentBrush = _WinAPI_CreateSolidBrush(0x0D47A1)  ; 진한 파란 액센트
        _WinAPI_FillRect($hDC, $tAccent, $hAccentBrush)
        _WinAPI_DeleteObject($hAccentBrush)
    EndIf

    ; ── 텍스트 그리기 ──
    Local $sText = ""
    If $iItemID >= 0 And $iItemID < UBound($g_aTabNames) Then
        $sText = $g_aTabNames[$iItemID]
    EndIf

    ; 폰트 생성
    Local $hFont = _WinAPI_CreateFont($TAB_FONT_SIZE + 3, 0, 0, 0, _
            ($bSelected ? 700 : 400), _  ; 선택 시 Bold
            False, False, False, _
            $DEFAULT_CHARSET, 0, 0, 5, 0, $TAB_FONT_NAME)
    Local $hOldFont = _WinAPI_SelectObject($hDC, $hFont)

    ; 텍스트 색상 및 배경 모드
    DllCall("gdi32.dll", "int", "SetBkMode", "handle", $hDC, "int", 1)  ; TRANSPARENT
    DllCall("gdi32.dll", "int", "SetTextColor", "handle", $hDC, _
            "int", $iTextColor)

    ; 텍스트 중앙 정렬 출력
    Local $tTextRect = DllStructCreate("long Left;long Top;long Right;long Bottom")
    DllStructSetData($tTextRect, "Left",   $iLeft)
    DllStructSetData($tTextRect, "Top",    $iTop)
    DllStructSetData($tTextRect, "Right",  $iRight)
    DllStructSetData($tTextRect, "Bottom", $iBottom)

    ; DT_CENTER=1, DT_VCENTER=4, DT_SINGLELINE=32
    DllCall("user32.dll", "int", "DrawTextW", _
            "handle", $hDC, _
            "wstr", $sText, _
            "int", -1, _
            "struct*", $tTextRect, _
            "uint", BitOR(1, 4, 32))

    _WinAPI_SelectObject($hDC, $hOldFont)
    _WinAPI_DeleteObject($hFont)

    Return True  ; 그리기 처리 완료
EndFunc

; ══════════════════════════════════════════════════════
; WM_NOTIFY 핸들러 - 호버 효과를 위한 마우스 추적
; ══════════════════════════════════════════════════════
Func _WM_NOTIFY($hWnd, $iMsg, $wParam, $lParam)
    Local $tNMHDR = DllStructCreate("hwnd hWndFrom;uint_ptr IDFrom;int Code", $lParam)
    Local $iCode = DllStructGetData($tNMHDR, "Code")

    ; TCN_SELCHANGE = -551 (탭 변경 시 강제 다시 그리기)
    If $iCode = -551 Then
        _WinAPI_InvalidateRect(GUICtrlGetHandle($g_hTab))
    EndIf

    Return $GUI_RUNDEFMSG
EndFunc

; ══════════════════════════════════════════════════════
; WinAPI 보조 함수
; ══════════════════════════════════════════════════════
Func _WinAPI_CreateSolidBrush($iColor)
    Local $aRet = DllCall("gdi32.dll", "handle", "CreateSolidBrush", "int", $iColor)
    Return $aRet[0]
EndFunc

Func _WinAPI_FillRect($hDC, $tRect, $hBrush)
    DllCall("user32.dll", "int", "FillRect", "handle", $hDC, "struct*", $tRect, "handle", $hBrush)
EndFunc

Comments