AmendHub

Download:

cyberslak

/

lightsout

/

amendments

/

13

main: add scroll support to main window and clean up entity access patterns

Because we now also have a scrollbar control, our control handling needs to be more sophisticated than simply iterating over all controls in a window. We now generally use list_foreach to iterate over entities. Additionally, we store some more info in reference constants (window_info and control_type).

cyberslak made amendment 13 24 days ago
--- main.c Sun Mar 9 23:43:56 2025 +++ main.c Tue Mar 11 00:00:49 2025 @@ -33,6 +33,7 @@ /* GLOBALS */ Boolean gRunning = true; short gNumBars = 0; +Boolean gHaveScrollBar = false; Handle gSliderCDEF, gEntityLDEF; PrefHandle gPreferences; @@ -67,6 +68,13 @@ pascal void dialog_list_update(WindowPtr theWin, short void memory_init(void); +void draw_bars(); +void draw_background(); +void update_bars(); + +void scrollbar_create(WindowPtr win); +void scrollbar_remove(WindowPtr win); + struct dialog_info { ListHandle lHnd; @@ -75,6 +83,17 @@ struct dialog_info void (*handle_activate)(bool activate, WindowPtr); }; +struct window_info +{ + ControlHandle scrollBar; +}; + +enum control_type +{ + CONTROL_SLIDER, + CONTROL_SCROLLBAR, +}; + void memory_init(void) { short i; @@ -159,6 +178,62 @@ void resolve_entity_ldef() entity_ldef->addr = entity_ldef_proc; } +void scrollbar_create(WindowPtr win) +{ + struct window_info *info = (void*)GetWRefCon(win); + Rect sbRect; + short winHeight = rect_height(&win->portRect); + short winWidth = rect_width(&win->portRect); + + sbRect.top = winHeight - kScrollBarWidth; + sbRect.left = -1; + sbRect.right = winWidth + 1; + sbRect.bottom = winHeight + 1; + + info->scrollBar = NewControl(win, + &sbRect, + (u8*)"", + true, + 0, + 0, + 1, + scrollBarProc, + CONTROL_SCROLLBAR); +} + +void scrollbar_remove(WindowPtr win) +{ + struct window_info *info = (void*)GetWRefCon(win); + + if (info->scrollBar) + { + DisposeControl(info->scrollBar); + } +} + +void handle_scrollbar(WindowPtr win) +{ + short width = rect_width(&win->portRect); + short height = rect_height(&win->portRect); + if (gNumBars > 3 && !gHaveScrollBar) + { + SizeWindow(win, width, height + kScrollBarWidth, true); + scrollbar_create(win); + gHaveScrollBar = true; + } else if (gNumBars > 3) + { + struct window_info *info = (void*)GetWRefCon(win); + SetControlMaximum(info->scrollBar, gNumBars - 3); + } + + if (gNumBars <= 3 && gHaveScrollBar) + { + SizeWindow(win, width, height - kScrollBarWidth, true); + scrollbar_remove(win); + gHaveScrollBar = false; + } +} + void create_bar(WindowPtr win, const char* id) { struct entity* ent = xmalloc(sizeof(struct entity)); @@ -171,6 +246,7 @@ void create_bar(WindowPtr win, const char* id) snprintf(ent->id, 128, "%s", id); winHeight = rect_height(&win->portRect); + winHeight -= (gHaveScrollBar ? kScrollBarWidth : 0); controlPos.top = (winHeight - kCtrlHeight)/2; controlPos.bottom = controlPos.top + kCtrlHeight; @@ -185,7 +261,7 @@ void create_bar(WindowPtr win, const char* id) 0, // current value 0, 254, kCustomSliderProc, - (long)ent + CONTROL_SLIDER ); ent->ctrl = ctrl; @@ -195,6 +271,8 @@ void create_bar(WindowPtr win, const char* id) gNumBars++; barOffset += kBarWidth; + + handle_scrollbar(win); } /** Update a list in a dialog. @@ -338,10 +416,14 @@ void lightsout_init(Handle previousEnts) short controlWidth = 40; short controlHeight = 80; short winWidth, winHeight; + struct window_info *wi = xmalloc(sizeof(struct window_info)); win = GetNewWindow(128, nil, (WindowPtr)-1); SetWTitle(win, "\pLights Out"); + SetWRefCon(win, (long)wi); + wi->scrollBar = NULL; + SetPort(win); win_center(win); @@ -420,8 +502,9 @@ void menu_click(long menuChoice) HiliteMenu(0); } -static void draw_control_value(ControlHandle ctrl) +static void draw_entity(struct entity *ent) { + ControlHandle ctrl = ent->ctrl; WindowPtr win = (*ctrl)->contrlOwner; char buf[128]; unsigned char* pBuf; @@ -429,11 +512,11 @@ static void draw_control_value(ControlHandle ctrl) Rect barBounds; short width; - struct entity *ent = (void*)GetControlReference(ctrl); + short scrollBarCompensation = (gHaveScrollBar ? kScrollBarWidth : 0); SetRect(&barBounds, sliderBounds.left - kCtrlPad, 0, sliderBounds.right + kCtrlPad, - win->portRect.bottom); + win->portRect.bottom - scrollBarCompensation); EraseRect(&barBounds); FrameRect(&barBounds); @@ -448,7 +531,7 @@ static void draw_control_value(ControlHandle ctrl) TextSize(10); MoveTo(barBounds.left + (kBarWidth - width)/2, - sliderBounds.bottom + 20); + sliderBounds.bottom + 20); DrawString(pBuf); @@ -456,70 +539,65 @@ static void draw_control_value(ControlHandle ctrl) pBuf = c2pstr(buf); width = StringWidth(pBuf); - MoveTo(barBounds.left + (kBarWidth - width) / 2, sliderBounds.top - 10); + MoveTo(barBounds.left + (kBarWidth - width) / 2, + sliderBounds.top - 10); + DrawString(pBuf); } -static void draw_bars(WindowPtr w) +void draw_bars(WindowPtr w) { WindowPeek win = (WindowPeek)w; - ControlHandle ctrl; - + struct window_info *info = (void*)GetWRefCon(w); + list_t *node; + struct entity *ent; - for (ctrl = win->controlList; ctrl; ctrl = (**ctrl).nextControl) + list_foreach(&gEntities, node) { - draw_control_value(ctrl); + ent = container_of(node, struct entity, node); + + draw_entity(ent); } } -static void update_bars() +void update_bars() { - ControlHandle ctrl; struct entity *ent; - struct entity_state *st; + list_t *node; static uint32_t lastTickCount = 0; WindowPeek win = (WindowPeek)gMainWindow; short val; GrafPtr oldPort; + bool changed = false; if (lastTickCount + kBarUpdateRate > TickCount()) return; - - if (FrontWindow() != gMainWindow) - return; - - if (!FrontWindow()) - return; - - //SysBeep(20); - - for (ctrl = win->controlList; ctrl; ctrl = (**ctrl).nextControl) + + list_foreach(&gEntities, node) { - ent = (void*)GetControlReference(ctrl); - st = &ent->state; - val = GetControlValue(ctrl); - if (!ent) - { - info("Invalid control reference for control %p", ctrl); - continue; - } + ent = container_of(node, struct entity, node); + val = GetControlValue(ent->ctrl); + ha_get_entity_state(ent->id, &ent->state); - ha_get_entity_state(ent->id, st); - - if (st->brightness != val) + if (ent->state.brightness != val) { - SetControlValue(ctrl, st->brightness); - GetPort(&oldPort); - SetPort(gMainWindow); - InvalRect(&gMainWindow->portRect); - SetPort(oldPort); + SetControlValue(ent->ctrl, ent->state.brightness); + changed = true; } } + if (changed) + { + GetPort(&oldPort); + SetPort(gMainWindow); + InvalRect(&gMainWindow->portRect); + SetPort(oldPort); + } + lastTickCount = TickCount(); } -static void draw_background(WindowPtr win) +void draw_background(WindowPtr win) { Rect drawRect; short height = rect_height(&win->portRect); @@ -534,6 +612,21 @@ static void draw_background(WindowPtr win) FrameRect(&drawRect); } +struct entity *entity_for_control(ControlHandle hnd) +{ + struct entity *ent; + list_t *node; + + list_foreach(&gEntities, node) + { + ent = container_of(node, struct entity, node); + if (ent->ctrl == hnd) + return ent; + } + + return NULL; +} + void event_update(WindowPtr win) { WindowPeek wPeek = (WindowPeek)win; @@ -552,6 +645,77 @@ void event_update(WindowPtr win) SetPort(oldPort); } +void update_entity_positions(WindowPtr win, short scrollValue) +{ + struct entity *ent; + list_t *node; + Rect curPos; + short i = 0; + + list_foreach(&gEntities, node) + { + ent = container_of(node, struct entity, node); + curPos = (**(ent->ctrl)).contrlRect; + MoveControl(ent->ctrl, + (i - scrollValue) * kBarWidth + kCtrlPad, + curPos.top); + + i++; + } +} + +void +control_mousedown(WindowPtr win, EventRecord* evt, + ControlHandle ctrl, short part) +{ + short orig_val = GetControlValue(ctrl); + if (part = TrackControl(ctrl, evt->where, nil)) + { + long ref = GetControlReference(ctrl); + switch (ref) + { + case CONTROL_SLIDER: + { + struct entity *ent = entity_for_control(ctrl); + short val = GetControlValue(ctrl); + InvalRect(&win->portRect); + ent->state.brightness = val; + ha_set_entity_state(ent->id, &ent->state); + break; + } + case CONTROL_SCROLLBAR: + { + short val = orig_val; + short max = GetControlMaximum(ctrl); + + switch (part) + { + case inPageUp: // left gray + case inUpButton: // left + val = MAX(0, val - 1); + break; + case inPageDown: // right gray + case inDownButton: // right + val = MIN(max, val + 1); + break; + case inThumb: + // control manager calculates new value for us + val = GetControlValue(ctrl); + break; + } + SetControlValue(ctrl, val); + + if (val != orig_val) + { + update_entity_positions(win, val); + InvalRect(&win->portRect); + } + break; + } + } + } +} + void event_mousedown(EventRecord* evt) { WindowPtr win; @@ -584,14 +748,9 @@ void event_mousedown(EventRecord* evt) } else { GlobalToLocal(&evt->where); inCtrlPart = FindControl(evt->where, win, &ctrl); - if (inCtrlPart && - (inCtrlPart = TrackControl(ctrl, evt->where, (ControlActionUPP)-1))) + if (inCtrlPart) { - short val = GetControlValue(ctrl); - struct entity *ent = (void*)GetControlReference(ctrl); - InvalRect(&win->portRect); - ent->state.brightness = val; - ha_set_entity_state(ent->id, &ent->state); + control_mousedown(win, evt, ctrl, inCtrlPart); } } break; @@ -629,9 +788,7 @@ void event_loop() while (gRunning) { - update_bars(); - - if (WaitNextEvent(everyEvent, &event, 10L, nil)) + if (WaitNextEvent(everyEvent, &event, 60L, nil)) { switch(event.what) { @@ -673,6 +830,8 @@ void event_loop() default: break; } + } else { + update_bars(); } } }