AmendHub

Download:

cyberslak

/

lightsout

/

amendments

/

16

Allow selecting and removing entities


cyberslak made amendment 16 22 days ago
--- entity.h Tue Mar 4 21:20:48 2025 +++ entity.h Tue Mar 11 21:42:12 2025 @@ -11,6 +11,8 @@ struct entity { char id[128]; struct entity_state state; + short selected; list_t node; ControlHandle ctrl; + RgnHandle outerRgn; }; --- LightsOut.r Mon Mar 10 23:54:56 2025 +++ LightsOut.r Tue Mar 11 21:11:02 2025 @@ -158,13 +158,15 @@ resource 'MENU' (130) { allEnabled, enabled, "Edit", - { /* array: 3 elements */ + { /* array: 4 elements */ /* [1] */ "Cut", noIcon, "X", noMark, plain, /* [2] */ "Copy", noIcon, "C", noMark, plain, /* [3] */ - "Paste", noIcon, "V", noMark, plain + "Paste", noIcon, "V", noMark, plain, + /* [4] */ + "Clear", noIcon, noKey, noMark, plain } }; --- list.h Fri Mar 7 23:21:34 2025 +++ list.h Wed Mar 12 00:19:29 2025 @@ -16,8 +16,26 @@ typedef struct list #define container_of(ptr, ty, field) \ ((ty*)((char*)(ptr)-offsetof(ty, field))) +// Iterate over a list. Note that items cannot be +// deleted while in this macro, because this will +// break the next link in the chain. If you wish +// to delete items, use list_foreach_safe at the +// expense of having to separately store the next ptr. #define list_foreach(head, node) \ for (node = (head)->next; node != (head); node = (node)->next) + + +// Iterate over a list. Requires an extra next ptr +// to make deletion safe. +#define list_foreach_safe(head, node, next_) \ + for (node = (head)->next, next_ = node->next; \ + node != (head); \ + node = next_, next_ = node->next) + +#define list_foreach_safe_rev(head, node, prev_) \ + for (node = (head)->prev, prev_ = node->prev; \ + node != (head); \ + node = prev_, prev_ = node->prev) static inline void list_init(list_t* lst) { --- main.c Tue Mar 11 00:16:10 2025 +++ main.c Wed Mar 12 01:18:31 2025 @@ -25,10 +25,12 @@ #define mApple 128 #define mFile 129 +#define mEdit 130 #define miAbout 1 #define miEntityAdd 1 #define miQuit 3 +#define miClear 4 /* GLOBALS */ Boolean gRunning = true; @@ -37,6 +39,7 @@ Boolean gHaveScrollBar = false; Handle gSliderCDEF, gEntityLDEF; PrefHandle gPreferences; +MenuHandle gEditMenu; Pattern gSliderPattern, gBackPattern; @@ -59,7 +62,7 @@ void menu_init(void); void menu_click(long); void lightsout_init(Handle previousEnts); -void create_bar(WindowPtr win, const char* id); +void bar_create(WindowPtr win, const char* id); void resolve_slider_cdef(); void resolve_entity_ldef(); @@ -68,13 +71,16 @@ pascal void dialog_list_update(WindowPtr theWin, short void memory_init(void); -void draw_bars(); +void bars_draw(); void draw_background(); -void update_bars(); +void bars_update(); void scrollbar_create(WindowPtr win); void scrollbar_remove(WindowPtr win); +void update_entity_positions(WindowPtr win, short scrollValue); +void get_entity_bounds(struct entity *ent, Rect* out); + struct dialog_info { ListHandle lHnd; @@ -234,38 +240,46 @@ void handle_scrollbar(WindowPtr win) } } -void create_bar(WindowPtr win, const char* id) +void bar_create(WindowPtr win, const char* id) { struct entity* ent = xmalloc(sizeof(struct entity)); Rect controlPos; + Rect updateBounds; ControlHandle ctrl; - static short barOffset = 0; short winHeight; - + short barOffset = gNumBars * kBarWidth; + 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; - controlPos.left = barOffset + (kBarWidth - kCtrlWidth)/2; + controlPos.left = barOffset + kCtrlPad; controlPos.right = controlPos.left + kCtrlWidth; - ctrl = NewControl( + ha_get_entity_state(ent->id, &ent->state); + + ent->ctrl = NewControl( win, &controlPos, nil, true, - 0, // current value + ent->state.brightness, // current value 0, 254, kCustomSliderProc, CONTROL_SLIDER ); - ent->ctrl = ctrl; + ent->outerRgn = NewRgn(); + ent->selected = false; + get_entity_bounds(ent, &updateBounds); + + updateBounds.right += 2; // bg edge + list_add_tail(&gEntities, &ent->node); gNumBars++; @@ -273,8 +287,42 @@ void create_bar(WindowPtr win, const char* id) barOffset += kBarWidth; handle_scrollbar(win); + + with_port(win, { + InvalRect(&updateBounds); + }) } +void bar_remove(WindowPtr win, struct entity *ent) +{ + struct window_info *info = (void*)GetWRefCon(win); + short scrollValue; + Rect barRect; + + if (info->scrollBar) + scrollValue = GetControlValue(info->scrollBar); + else + scrollValue = 0; + + get_entity_bounds(ent, &barRect); + + list_del(&ent->node); + DisposeControl(ent->ctrl); + DisposeRgn(ent->outerRgn); + free(ent); + + // you can't just update the bar rect, + // because other bars might shift into the window area + with_port(win, { + InvalRect(&win->portRect); + }) + + gNumBars--; + + update_entity_positions(win, scrollValue); + handle_scrollbar(win); +} + /** Update a list in a dialog. * * Assumes the list is stored in the window reference of the @@ -297,7 +345,7 @@ pascal void dialog_list_update(WindowPtr theWin, short void dialog_info_free(struct dialog_info *dinfo) { list_t *node; - LDispose(dinfo->lHnd); + LDispose(dinfo->lHnd); while ((node = list_pop(&dinfo->ents)) != NULL) { @@ -315,7 +363,7 @@ void entity_list_add(struct dialog_info* dinfo) { short len = 4; LGetCell(&ent, &len, pt, dinfo->lHnd); - create_bar(gMainWindow, ent->id); + bar_create(gMainWindow, ent->id); } } @@ -418,7 +466,7 @@ void lightsout_init(Handle previousEnts) short winWidth, winHeight; struct window_info *wi = xmalloc(sizeof(struct window_info)); - win = GetNewWindow(128, nil, (WindowPtr)-1); + win = GetNewCWindow(128, nil, (WindowPtr)-1); SetWTitle(win, "\pLights Out"); SetWRefCon(win, (long)wi); @@ -442,14 +490,13 @@ void lightsout_init(Handle previousEnts) while (idx < sz) { len = strlen(*previousEnts + idx); - create_bar(win, *previousEnts + idx); + bar_create(win, *previousEnts + idx); idx += len + 1; } HUnlock(previousEnts); } - update_bars(); ShowWindow(win); } @@ -457,15 +504,54 @@ void menu_init(void) { Handle menuBar = GetNewMBar(128); MenuHandle appleMenu; + short i; SetMenuBar(menuBar); + gEditMenu = GetMenuHandle(mEdit); + + for (i = 1; i <= miClear; ++i) + DisableItem(gEditMenu, i); + appleMenu = GetMenuHandle(mApple); AppendResMenu(appleMenu, 'DRVR'); DrawMenuBar(); } +bool menu_item_enabled(MenuHandle hnd, short itemId) +{ + if (itemId < 32) + { + return (**hnd).enableFlags & (1 << itemId); + } + return (**hnd).enableFlags & 1; +} + +void menu_update() +{ + struct entity *ent; + list_t *node; + bool any_selected = false; + bool itemEnabled = menu_item_enabled(gEditMenu, miClear); + + list_foreach(&gEntities, node) + { + ent = container_of(node, struct entity, node); + if (ent->selected) + any_selected = true; + } + + if (any_selected && !itemEnabled) + { + EnableItem(gEditMenu, miClear); + } + else if (!any_selected && itemEnabled) + { + DisableItem(gEditMenu, miClear); + } +} + void menu_click(long menuChoice) { short menuId, itemId; @@ -500,39 +586,64 @@ void menu_click(long menuChoice) gRunning = false; } break; + case mEdit: + if (itemId == miClear && FrontWindow() == gMainWindow) + { + struct entity *ent; + list_t *node, *prev; + list_foreach_safe_rev(&gEntities, node, prev) + { + ent = container_of(node, struct entity, node); + if (ent->selected) + bar_remove(gMainWindow, ent); + } + } + break; } HiliteMenu(0); } -static void draw_entity(struct entity *ent) +void get_entity_bounds(struct entity *ent, Rect* out) { + Rect* sliderBounds; + short scrollBarCompensation = (gHaveScrollBar ? kScrollBarWidth : 0); + WindowPtr win = (**(ent->ctrl)).contrlOwner; + + HLock((Handle)ent->ctrl); + sliderBounds = &(*(ent->ctrl))->contrlRect; + HUnlock((Handle)ent->ctrl); + + SetRect(out, sliderBounds->left - kCtrlPad, + 0, sliderBounds->right + kCtrlPad, + win->portRect.bottom - scrollBarCompensation); +} + +static void bar_draw(struct entity *ent) +{ ControlHandle ctrl = ent->ctrl; WindowPtr win = (*ctrl)->contrlOwner; char buf[128]; unsigned char* pBuf; Rect sliderBounds = (*ctrl)->contrlRect; Rect barBounds; - short width; - short scrollBarCompensation = (gHaveScrollBar ? kScrollBarWidth : 0); + short val = GetControlValue(ctrl); - SetRect(&barBounds, sliderBounds.left - kCtrlPad, - 0, sliderBounds.right + kCtrlPad, - win->portRect.bottom - scrollBarCompensation); - + TextFont(1); + TextSize(10); + + get_entity_bounds(ent, &barBounds); + EraseRect(&barBounds); FrameRect(&barBounds); snprintf(buf, 128, "%d%%", - (GetControlValue(ctrl) * 100 + 127) / 255); + (val * 100 + 127) / 255); pBuf = c2pstr(buf); width = StringWidth(pBuf); - TextFont(1); - TextSize(10); - MoveTo(barBounds.left + (kBarWidth - width)/2, sliderBounds.bottom + 20); @@ -546,9 +657,21 @@ static void draw_entity(struct entity *ent) sliderBounds.top - 10); DrawString(pBuf); + + SetRectRgn(ent->outerRgn, 0, 0, 0, 0); + + OpenRgn(); + FrameRect(&barBounds); + FrameRoundRect(&sliderBounds, 15, 15); + CloseRgn(ent->outerRgn); + + if (ent->selected) + { + InvertRect(&barBounds); + } } -void draw_bars(WindowPtr w) +void bars_draw(WindowPtr w) { WindowPeek win = (WindowPeek)w; struct window_info *info = (void*)GetWRefCon(w); @@ -559,11 +682,11 @@ void draw_bars(WindowPtr w) { ent = container_of(node, struct entity, node); - draw_entity(ent); + bar_draw(ent); } } -void update_bars() +void bars_update() { struct entity *ent; list_t *node; @@ -640,7 +763,7 @@ void event_update(WindowPtr win) if (win == gMainWindow) { - draw_bars(win); + bars_draw(win); draw_background(win); UpdateControls(win, win->visRgn); } @@ -719,6 +842,24 @@ control_mousedown(WindowPtr win, EventRecord* evt, } } +struct entity* find_entity_for_click(WindowPtr win, Point where) +{ + struct entity *ent; + list_t *node; + + if (win != gMainWindow) + return; + + list_foreach(&gEntities, node) + { + ent = container_of(node, struct entity, node); + if (PtInRgn(where, ent->outerRgn)) + return ent; + } + + return NULL; +} + void event_mousedown(EventRecord* evt) { WindowPtr win; @@ -749,12 +890,21 @@ void event_mousedown(EventRecord* evt) struct dialog_info* dinfo = (void*)GetWRefCon(win); dinfo->handle_mousedown(evt, win); } else { + struct entity* ent; GlobalToLocal(&evt->where); inCtrlPart = FindControl(evt->where, win, &ctrl); if (inCtrlPart) { control_mousedown(win, evt, ctrl, inCtrlPart); } + + if ((ent = find_entity_for_click(win, evt->where))) + { + Rect entityBounds; + ent->selected = !ent->selected; + get_entity_bounds(ent, &entityBounds); + InvalRect(&entityBounds); + } } break; case inDrag: @@ -834,7 +984,8 @@ void event_loop() break; } } else { - update_bars(); + menu_update(); + bars_update(); } } } --- slider.c Mon Mar 10 22:59:43 2025 +++ slider.c Wed Mar 12 00:56:37 2025 @@ -2,6 +2,9 @@ #include "slider.h" extern Pattern gSliderPattern; +RGBColor gLightGrey = { + 60000, 60000, 60000 +}; /** Custom slider control * @@ -30,7 +33,7 @@ slider_proc(short varCode, ControlHandle ctl, recalc: trackRect = (**ctl).contrlRect; trackFilledRect = trackRect; - filledPct = (**ctl).contrlValue * 100 / (**ctl).contrlMax; + filledPct = ((**ctl).contrlValue * 100)/ (**ctl).contrlMax; trackHeight = rect_height(&trackRect) * filledPct / 100; trackFilledRect.top = trackRect.bottom - trackHeight; @@ -47,8 +50,19 @@ recalc: { case drawCntl: EraseRoundRect(&trackRect, 15, 15); + RGBBackColor(&gLightGrey); FillRoundRect(&trackRect, 15, 15, &gSliderPattern); - FrameRoundRect(&trackRect,15, 15); + BackColor(whiteColor); + FrameRoundRect(&trackRect, 13, 13); + + ForeColor(whiteColor); + rect_expand(&trackRect, 1); + FrameRoundRect(&trackRect, 15, 15); + + ForeColor(blackColor); + rect_expand(&trackRect, 1); + FrameRoundRect(&trackRect, 18, 18); + PaintRoundRect(&trackFilledRect, 15, 15); if ((**ctl).contrlHilite == 129) { @@ -92,7 +106,7 @@ recalc: case posCntl: vertOff = HiWord(param); valueDelta = (-vertOff * (**ctl).contrlMax) / rect_height(&(**ctl).contrlRect); - (**ctl).contrlValue += valueDelta; + SetControlValue(ctl, GetControlValue(ctl) + valueDelta); // we need to redraw; just pretend we were called with // different args param = 0; --- util.h Sun Mar 9 23:43:39 2025 +++ util.h Wed Mar 12 00:36:12 2025 @@ -6,6 +6,15 @@ #define MIN(x, y) ((x) > (y) ? (y) : (x)) #define MAX(x, y) ((x) > (y) ? (x) : (y)) +#define with_port(port, block) \ + { \ + GrafPtr oldPort; \ + GetPort(&oldPort); \ + SetPort(port); \ + block \ + SetPort(oldPort); \ + } + typedef struct { short push[2], rts;