Last active
March 26, 2025 02:49
-
-
Save mingrammer/4094fc32da70fc69a8b84ec69bf8da33 to your computer and use it in GitHub Desktop.
Button click handler
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"time" | |
) | |
type eventType int | |
const ( | |
downUp eventType = iota | |
upDown | |
) | |
func (et eventType) String() string { | |
switch et { | |
case downUp: | |
return "Down Up" | |
case upDown: | |
return "Up Down" | |
default: | |
return "Unknown" | |
} | |
} | |
type clickType int | |
const ( | |
singleClick clickType = iota | |
doubleClick | |
) | |
func (ct clickType) String() string { | |
switch ct { | |
case singleClick: | |
return "Single Click" | |
case doubleClick: | |
return "Double Click" | |
default: | |
return "Unknown" | |
} | |
} | |
const doubleClickMs = 500 | |
type input struct { | |
buttonID int | |
tsMs int | |
eventTyp eventType | |
value string | |
} | |
type queue []input | |
type output struct { | |
clickTyp clickType | |
tsMs int | |
} | |
type buttonState struct { | |
lastClickTime int | |
clickCount int | |
timer *time.Timer | |
} | |
func handleEvent( | |
event input, buttonStates map[int]*buttonState, results map[int][]output, | |
) { | |
state, ok := buttonStates[event.buttonID] | |
if !ok { | |
state = &buttonState{} | |
buttonStates[event.buttonID] = state | |
} | |
// 이벤트 타입이 downUp 경우 무시 (클릭 완성의 판정은 upDown으로 함) | |
// 이 케이스는 더블 클릭을 판정하는 중간에 다른 버튼이 눌렸을 때, 기존 버튼의 상태를 초기화하기 위함. | |
if event.eventTyp == downUp { | |
// 다른 버튼이 눌렸을 때, 기존 버튼 상태 초기화 | |
for key, btnState := range buttonStates { | |
if key != event.buttonID && btnState.clickCount == 1 && btnState.timer != nil { | |
btnState.timer.Stop() // 타이머 종료 | |
btnState.clickCount = 0 | |
results[key] = append(results[key], | |
output{ | |
clickTyp: singleClick, | |
tsMs: btnState.lastClickTime, | |
}) | |
} | |
} | |
} | |
// upDown 이벤트 처리 (클릭 판정) | |
if event.eventTyp == upDown { | |
// 다른 버튼 클릭 판정시, 기존 버튼 상태 초기화 | |
for key, btnState := range buttonStates { | |
if key != event.buttonID && btnState.clickCount == 1 && btnState.timer != nil { | |
btnState.timer.Stop() // 타이머 종료 | |
btnState.clickCount = 0 | |
results[key] = append(results[key], | |
output{ | |
clickTyp: singleClick, | |
tsMs: btnState.lastClickTime, | |
}) | |
} | |
} | |
// 첫 번째 클릭인지 확인 | |
if state.clickCount == 0 { | |
state.lastClickTime = event.tsMs | |
state.clickCount = 1 | |
// Double Click 대기 타이머 시작 | |
state.timer = time.AfterFunc(doubleClickMs*time.Millisecond, func() { | |
// doubleClickMs 이내에 두 번째 클릭이 없을 경우 Single Click으로 확정 | |
if state.clickCount == 1 { | |
results[event.buttonID] = append(results[event.buttonID], | |
output{ | |
clickTyp: singleClick, | |
tsMs: state.lastClickTime, | |
}) | |
} | |
state.clickCount = 0 | |
}) | |
} else if state.clickCount == 1 { | |
// 두 번째 downUp 이벤트시 시간 차이를 확인하여 Double Click 판정 | |
elapsedTime := event.tsMs - state.lastClickTime | |
if elapsedTime <= doubleClickMs { | |
// Double Click 발생 | |
if state.timer != nil { | |
state.timer.Stop() // 타이머 종료 | |
} | |
results[event.buttonID] = append( | |
results[event.buttonID], output{ | |
clickTyp: doubleClick, | |
tsMs: event.tsMs, | |
}) | |
} else { | |
// 시간 차이가 doubleClickMs 이상인 경우 Single Click으로 확정하고 새 클릭 처리 | |
results[event.buttonID] = append(results[event.buttonID], | |
output{ | |
clickTyp: singleClick, | |
tsMs: state.lastClickTime, | |
}) | |
state.lastClickTime = event.tsMs | |
state.clickCount = 1 | |
state.timer = time.AfterFunc(doubleClickMs*time.Millisecond, func() { | |
if state.clickCount == 1 { | |
results[event.buttonID] = append(results[event.buttonID], | |
output{ | |
clickTyp: singleClick, | |
tsMs: state.lastClickTime, | |
}) | |
} | |
state.clickCount = 0 | |
}) | |
return | |
} | |
state.clickCount = 0 // 상태 초기화 | |
} | |
} | |
} | |
func process(events queue) map[int][]output { | |
results := make(map[int][]output) | |
// Button State 초기화 | |
buttonStates := make(map[int]*buttonState) | |
for _, event := range events { | |
handleEvent(event, buttonStates, results) | |
} | |
return results | |
} | |
func main() { | |
examples := []struct { | |
description string | |
q queue | |
}{ | |
{ | |
"Single Click 1", | |
queue{ | |
{1, 1000, upDown, "a"}, | |
{1, 1100, downUp, "a"}, | |
{2, 2000, upDown, "a"}, | |
{2, 2100, downUp, "a"}, | |
}, | |
}, | |
{ | |
"Single Click 2", | |
queue{ | |
{1, 1000, upDown, "a"}, | |
{2, 1100, upDown, "a"}, | |
{3, 1200, upDown, "a"}, | |
{4, 1300, upDown, "a"}, | |
{1, 1400, downUp, "a"}, | |
{2, 1500, downUp, "a"}, | |
{3, 1600, downUp, "a"}, | |
{4, 1700, downUp, "a"}, | |
}, | |
}, | |
{ | |
"Double Click 1", | |
queue{ | |
{1, 1000, upDown, "a"}, | |
{1, 1100, downUp, "a"}, | |
{1, 1200, upDown, "a"}, | |
{1, 1300, downUp, "a"}, | |
}, | |
}, | |
{ | |
"Double Click 2", | |
queue{ | |
{1, 1000, upDown, "a"}, | |
{1, 1200, downUp, "a"}, | |
{1, 1300, upDown, "a"}, | |
{1, 2000, downUp, "a"}, | |
}, | |
}, | |
{ | |
"두 개의 빠른 클릭간 시간차가 500ms 초과인 경우", | |
queue{ | |
{1, 1000, upDown, "a"}, | |
{1, 1100, downUp, "a"}, | |
{1, 1550, upDown, "a"}, | |
{1, 1650, downUp, "a"}, | |
}, | |
}, | |
{ | |
"클릭간 시간차가 500ms 이하지만, 중간에 다른 버튼의 upDown 이 발생한 경우", | |
queue{ | |
{1, 1000, upDown, "a"}, | |
{1, 1100, downUp, "a"}, | |
{2, 1200, upDown, "a"}, | |
{1, 1400, upDown, "a"}, | |
{1, 1500, downUp, "a"}, | |
}, | |
}, | |
{ | |
"클릭간 시간차가 500ms 이하지만, 중간에 다른 버튼의 downUp 이 발생한 경우", | |
queue{ | |
{1, 1000, upDown, "a"}, | |
{1, 1100, downUp, "a"}, | |
{2, 1300, downUp, "a"}, | |
{1, 1400, upDown, "a"}, | |
{1, 1500, downUp, "a"}, | |
}, | |
}, | |
{ | |
"한 버튼의 두 번째 downUp 이벤트가 다른 버튼의 입력 이벤트 이후 발생한 경우", | |
queue{ | |
{1, 1000, upDown, "a"}, | |
{1, 1100, downUp, "a"}, | |
{1, 1200, upDown, "a"}, | |
{2, 1300, upDown, "a"}, | |
{1, 1400, downUp, "a"}, | |
}, | |
}, | |
{ | |
"한 버튼의 입력 이벤트 사이에 다른 버튼의 Double Click 이 발생한 경우", | |
queue{ | |
{1, 1000, upDown, "a"}, | |
{1, 1100, downUp, "a"}, | |
{3, 1200, upDown, "a"}, | |
{3, 1300, downUp, "a"}, | |
{3, 1400, upDown, "a"}, | |
{3, 1500, downUp, "a"}, | |
{1, 1600, upDown, "a"}, | |
{1, 1700, downUp, "a"}, | |
}, | |
}, | |
{ | |
"빠르게 세번 클릭한 경우 (Double Click 과 Single Click 이 차례로 발생)", | |
queue{ | |
{1, 1000, upDown, "a"}, | |
{1, 1100, downUp, "a"}, | |
{1, 1200, upDown, "a"}, | |
{1, 1300, downUp, "a"}, | |
{1, 1400, upDown, "a"}, | |
{1, 1500, downUp, "a"}, | |
}, | |
}, | |
} | |
for i, e := range examples { | |
fmt.Println("-----------------------------------------------") | |
fmt.Printf("Example %d: %s\n", i+1, e.description) | |
fmt.Println("-----------------------------------------------") | |
fmt.Println() | |
fmt.Println("-----------------------------------------------") | |
fmt.Println("Queue") | |
fmt.Println("-----------------------------------------------") | |
for _, o := range e.q { | |
fmt.Printf( | |
"- btn id: %d, ts: %d, event: %s, value: %s\n", | |
o.buttonID, o.tsMs, o.eventTyp, o.value, | |
) | |
} | |
fmt.Println("-----------------------------------------------") | |
fmt.Println() | |
fmt.Println("-----------------------------------------------") | |
fmt.Println("Output") | |
fmt.Println("-----------------------------------------------") | |
result := process(e.q) | |
for k, v := range result { | |
fmt.Printf("btn id: %d\n", k) | |
for _, o := range v { | |
fmt.Printf("- click type: %s, ts: %d\n", o.clickTyp, o.tsMs) | |
} | |
} | |
fmt.Println("-----------------------------------------------") | |
fmt.Println() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment