schrockwell
/PopUp
/amendments
/1
Add initial PopUp library and README
schrockwell made amendment 1 5 days ago
--- PopUp.c Wed Oct 15 18:31:27 2025
+++ PopUp.c Wed Oct 15 18:31:27 2025
@@ -0,0 +1,182 @@
+#include "PopUp.h"
+
+PopUpPtr CreatePopUp(WindowPtr window, Rect *bounds, MenuHandle menu, short style)
+{
+ PopUpPtr popup;
+
+ popup = (PopUpPtr)NewPtr(sizeof(PopUpRec));
+ if (popup == nil)
+ return nil;
+
+ InsertMenu(menu, -1L);
+
+ popup->menu = menu;
+ popup->window = window;
+ popup->item = 1;
+ popup->bounds = *bounds;
+ popup->style = style;
+
+ return popup;
+}
+
+void DisposePopUp(PopUpPtr popup)
+{
+ if (popup == nil)
+ return;
+
+ DisposPtr((Ptr)popup);
+}
+
+void DrawPopUp(PopUpPtr popup)
+{
+ GrafPtr prevPort;
+
+ GetPort(&prevPort);
+ SetPort(popup->window);
+
+ // draw border
+ FrameRect(&popup->bounds);
+
+ // draw shadow
+ MoveTo(popup->bounds.left + 1, popup->bounds.bottom);
+ LineTo(popup->bounds.right, popup->bounds.bottom);
+ LineTo(popup->bounds.right, popup->bounds.top + 1);
+
+ // draw text and triangle
+ _DrawPopUpItem(popup);
+
+ SetPort(prevPort);
+}
+
+void _DrawPopUpItem(PopUpPtr popup)
+{
+ Str255 menuItem;
+ Rect bgRect;
+ RgnHandle prevClip;
+
+ PolyHandle triangle;
+ Point pt1, pt2, pt3;
+
+ if (popup->style & kPopUpStyleText)
+ {
+ // determine text
+ GetItem(popup->menu, popup->item, &menuItem);
+
+ // fill bg with white
+ bgRect = popup->bounds;
+ InsetRect(&bgRect, 1, 1);
+ FillRect(&bgRect, white);
+
+ // draw item text
+ MoveTo(
+ popup->bounds.left + 5, // left margin
+ popup->bounds.bottom - 5 // bottom margin
+ );
+ TextFont(0);
+ DrawString(menuItem);
+ }
+
+ if (popup->style & kPopUpStyleCaret)
+ {
+ // draw disclosure triangle
+ pt1.h = popup->bounds.right - 17;
+ pt1.v = popup->bounds.top + 6;
+
+ pt2.h = popup->bounds.right - 5;
+ pt2.v = pt1.v;
+
+ pt3.h = popup->bounds.right - 11;
+ pt3.v = popup->bounds.top + 13;
+
+ triangle = OpenPoly();
+ MoveTo(pt1.h, pt1.v);
+ LineTo(pt2.h, pt2.v);
+ LineTo(pt3.h, pt3.v);
+ LineTo(pt1.h, pt1.v);
+ ClosePoly();
+
+ FillPoly(triangle, black);
+ KillPoly(triangle);
+ }
+}
+
+Boolean HandlePopUpEvent(PopUpPtr popup, EventRecord *eventPtr)
+{
+ WindowPtr window;
+ long thePart;
+ long menuChoice;
+
+ if (eventPtr->what != mouseDown)
+ return false;
+
+ thePart = FindWindow(eventPtr->where, &window);
+
+ if (!(window == popup->window && thePart == inContent))
+ return false;
+
+ return _HandlePopUpMouseDownInContent(popup, eventPtr);
+}
+
+Boolean _HandlePopUpMouseDownInContent(PopUpPtr popup, EventRecord *eventPtr)
+{
+ Point eventPoint, popUpPoint;
+ short choice;
+ Rect invertBounds;
+ GrafPtr prevPort;
+
+ eventPoint = eventPtr->where;
+ GlobalToLocal(&eventPoint);
+
+ if (PtInRect(eventPoint, &popup->bounds))
+ {
+ GetPort(&prevPort);
+ SetPort(popup->window);
+
+ invertBounds = popup->bounds;
+ InsetRect(&invertBounds, 1, 1);
+
+ popUpPoint.v = popup->bounds.top + 1;
+ popUpPoint.h = popup->bounds.left + 1;
+ LocalToGlobal(&popUpPoint);
+
+ InvertRect(&invertBounds);
+
+ if (popup->style & kPopUpStyleCheck)
+ CheckItem(popup->menu, popup->item, true);
+
+ choice = PopUpMenuSelect(
+ popup->menu,
+ popUpPoint.v,
+ popUpPoint.h,
+ popup->item
+ );
+
+ InvertRect(&invertBounds);
+
+ if (popup->style & kPopUpStyleCheck)
+ CheckItem(popup->menu, popup->item, false);
+
+ SetPort(prevPort);
+
+ if (LoWord(choice) > 0)
+ {
+ SelectPopUpItem(popup, LoWord(choice));
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void SelectPopUpItem(PopUpPtr popup, short item)
+{
+ GrafPtr prevPort;
+
+ GetPort(&prevPort);
+ SetPort(popup->window);
+
+ popup->item = item;
+ _DrawPopUpItem(popup);
+
+ SetPort(prevPort);
+}
--- PopUp.h Wed Oct 15 18:33:02 2025
+++ PopUp.h Wed Oct 15 18:33:02 2025
@@ -0,0 +1,38 @@
+#ifndef __POPUP_H__
+#define __POPUP_H__
+
+#define kPopUpStyleText 0x01 // draw current item text
+#define kPopUpStyleCaret 0x02 // draw disclosure triangle
+#define kPopUpStyleCheck 0x04 // check current item in menu
+#define kPopUpStyleAll 0xFF // all of the above
+
+typedef struct PopUpRec
+{
+ WindowPtr window;
+ MenuHandle menu;
+ Rect bounds;
+ short item;
+ short style;
+} PopUpRec, *PopUpPtr, **PopUpHandle;
+
+// Returns a newly-allocated pointer to a PopUpRec
+PopUpPtr CreatePopUp(WindowPtr window, Rect *bounds, MenuHandle menu, short style);
+
+// Frees memory
+void DisposePopUp(PopUpPtr popup);
+
+// Paints the control frame, text, and other elements. Must be called
+// during the window update cycle, between BeginUpdate() and EndUpdate()
+void DrawPopUp(PopUpPtr popup);
+
+// Hook to handle mouse-down events that trigger the menu. Must be called
+// in the main event loop. Returns true if the event was handled.
+Boolean HandlePopUpEvent(PopUpPtr popup, EventRecord *eventPtr);
+
+// Changes the currently-selected item index and repaints the control
+void SelectPopUpItem(PopUpPtr popup, short item);
+
+// Library use only
+void _DrawPopUpItem(PopUpPtr popup);
+Boolean _HandlePopUpMouseDownInContent(PopUpPtr popup, EventRecord *eventPtr);
+#endif
--- README Wed Oct 15 18:35:43 2025
+++ README Wed Oct 15 18:35:43 2025
@@ -0,0 +1,79 @@
+ Easy Pop-Up Menus for System 6!
+ Rockwell Schrock <rockwell@schrock.me>
+ https://schrockwell.com
+
+This library makes it more convenient to add pop-up menus to System 6
+applications.
+
+In System 7 and later, you can define a control with procID 1008, but
+prior to that, some hand-holding is required to achieve the expected
+control behavior.
+
+These functions take care of the grunt work for System 6:
+
+- drawing the control
+- opening the menu with PopUpMenuSelect()
+- storing the selected item
+
+To use it, you only need to provide:
+
+- a WindowPtr for the containing window
+- a Rect for the bounds of the menu control
+- a MenuHandle to the menu
+- styling options
+
+...and add hooks in three places:
+
+1. Initialization
+2. Event loop
+3. Window update
+
+See PopUp.h for more documentation and styling options.
+
+=== Example ===
+
+// 1. Initialization
+
+PopUpPtr gPopup; // <---
+
+void WindowInit()
+{
+ WindowPtr window;
+ Rect windRect;
+ Rect popupRect = { 10, 10, 29, 120 }; // <--- recommend 19px height
+ MenuHandle menu;
+
+ window = GetNewWindow(kBaseResID, nil, kMoveToFront);
+ menu = GetMenu(kPopUpMenuResourceID);
+
+ gPopup = CreatePopUp(window, &popupRect, menu, kPopUpStyleAll); // <---
+
+ SetPort(window);
+ ShowWindow(window);
+}
+
+// 2. Event loop
+
+void DoEvent(EventRecord *eventPtr)
+{
+ if (HandlePopUpEvent(gPopup, eventPtr)) // <---
+ {
+ // gPopup->item contains the newly-selected item
+ return;
+ }
+
+ // ...rest of event handlers...
+}
+
+// 3. Window updates
+
+void DoUpdate(EventRecord *eventPtr)
+{
+ WindowPtr window = (WindowPtr)eventPtr->message;
+
+ BeginUpdate(window);
+
+ DrawPopUp(gPopup); // <---
+
+ EndUpdate(window);
+}