crunch@well.UUCP (John Draper) (12/12/85)
PHUN WITH GADGETS - John T. Draper ---------------------------------- Feel free to distribute this information onto any network. Please check first to avoid duplication. Would at least like credit for the 20 hours or so I took in preparing this section. Gadgets are multi function input "devices" which run under Intuition. There are four kinds, and two flavers. The flavers are: 1) System gadgets - Those created for you when you create a window or screen 2) User gadgets - Those gadgets YOU create and use in your program. There are four kinds of these. The four kinds are: 1) Boolean Gadgets - Usually "Yes/no" desisions, "On/Off" buttons, and other related uses. They look like rectangles, with text displayed (Or rendered), within those rectangles. 2) Proportional Gadgets - Those which look like "Sliders" as displayed in the "R G B" Controls in the "Preferences" program. I think Amiga should release the source to Preferences, because It would be a very good example program. 3) String gadgets - These allow text entry through the Keyboard. Text can be automatically centered. 4) Integer Gadget - A special form of string gadget that converts numeric text input into integers. USING GADGETS ------------- Each gadget has a data structure "Gadget", which must first be declared and initialized. There are several ways to do this. The easiest way is to declare and initialize them like this: #define BLUE_GADGET 0 /* My own Gadget ID to mean this gadget */ struct Gadget blue_gad = { NL, 17,112, 150,11, GADGHCOMP, GADGIMMEDIATE | RELVERIFY, PROPGADGET,(APTR)&b_img, NL, &btxt, NL,(APTR)&b_prop, BLUE_GADGET, NL }; #define GREEN_GADGET 1 /* Another personal ID */ struct Gadget green_gad = { &blue_gad, 17,97, 120,11, GADGHCOMP, GADGIMMEDIATE | RELVERIFY, PROPGADGET,(APTR)&g_img, NL, >xt, NL,(APTR)&g_prop, GREEN_GADGET, NL }; #define RED_GADGET 2 /* And yet another gadget */ struct Gadget red_gad = { &green_gad, 17,82, 90,11, GADGHCOMP, GADGIMMEDIATE | RELVERIFY, PROPGADGET,(APTR)&r_img, NL, &rtxt, NL,(APTR)&r_prop, RED_GADGET, NL }; #define TEX_GAD 3 struct Gadget tex_gad = { &red_gad, 10,10, 150,11, GADGHCOMP, STRINGCENTER | LONGINT, STRGADGET, NL, NL, NL, NL, (APTR)&TexString, TEX_GAD, NL }; The #define BLUE_GADGET 0, etc, are my own personal numbers that I want to use to identify these gadgets in my very own personal way. I am using them for identification and to take action when the gadget is chosen via a C "Switch" statement. Each "Element" in the structures declared above are separated by commas (For those not familiar with C), and will be explained in detail. In the above examples are Three Proportional Gadgets, and a Text Gadget. A more detailed description of each of the gadget fields are listed below. It is pretty well explained in the Intuition manual. Just a lack of examples showing how to build and detect them. -- (NextGadget) First element is the "link" to the next gadget. Last gadget has a Null (NL) for a link. Note, the last gadget is on the top. The LAST Gadget should be set to NULL (NL). -- (LeftEdge, TopEdge) - Specifies the LOCATION of the gadget relative to the window or screen, in the above example, "tex_gad.LeftEdge" has the value of 10. The last gadget has LeftEdge = 17, TopEdge = 112 as shown above. -- (Width, Height) - Specifies the Width and the height of the gadget select box. In the above example, the height remained the same, and the Width progressively got smaller. Width = 150, Height = 11 on the last one, then Width = 120, then eventually 90, See above. -- (Flags) - Flags that you share with Intuition.to describe the appearance and behavior of the gadget. GADGHCOMP is a flag that complements all of the bits contained within the gadget's select box. -- (Activation) - Other flags that are used to activate certain features of the gadget. GADGIMMEDIATE | RELVERIFY mean that we set BOTH those "Bits". -- (GadgetType) - Describes the type of gadget. In the example above, STRGADGET is one of the four types of gadgets that describe "string" or "Text". -- (GadgetRender) - Points to an intuiImage. This is what gets written onto screen or window as the gadget. In the case where we use the proportional gadget, we supply the address of the Image Structure without initializing it. It then gets rendered as a rectangle. -- (SelectRender) - In this example, we put a NULL (NL) in this field. If we wanted, we could put in another image. This image would be displayed during highlighting (When mouse button is pressed in the gadget's select box). -- (GadgetText) - A pointer to an "Intuitext" structure that describes any text that might be rendered when the gadget is rendered. -- (MutualExclude) - In this example, we place a NULL (NL). There are 32 bits that can be used to "Turn off" other gadgets if this gadget gets selected. -- (SpecialInfo) - A pointer to more information about proportional (PropInfo) and text (StringInfo). gadgets. -- (GadgetID) - User definable ID Field. It can be used to identify the gadget from the gadget pointer. I use it as an argument to the "switch" statement, because using an address (Or any large number) as arguments to the "switch" statement will cause the system to crash. It's a bug in the Lattice C compiler. -- (UserData) - A pointer to data that can be of any user definable structure or data. There are other structures needed for gadgets which are "SpecialInfo" types of structures. They are: PropInfo, and StringInfo structures. These describe in detail more information on Proportional structures and Text structures. Addresses of these structures will be put in the "SpecialInfo" field of the gadget structure. For instance, in the example above where the gadgets are declared and initialized, (APTR)&r_img is used in the "red_gad" declaration. Where "r_img" is declared as follows: struct Image r_img, g_img, b_img; In this particular example, these are Images used by Intuition. Sometimes YOU have to declare your own image. Other times, such as in the case above, Intuition initializes these images. On first reading of the Intuition manual, it is not evident that These are initialized. PropInfo Structure: ------------------- A PropInfo structure contains specific information about a proportional gadget. Such as a slider arm, or a volume control. -- (flags) - A set of flags like: AUTOKNOB - If you want to use a generic default knob. It's essentually a rectangle that fills the body of the proportional gadget. If you set this flag, Intuition will initialize the rest of this structure. A piece of code in the Example GAD.C shows where Three proportional structures are initialized. These structures are named: "r_prop", "g_prop", and "b_prop". /*************************************************************************** Must Initialize the Proportional "Specialinfo" before opening up the window. Init Flags, and position. ***************************************************************************/ r_prop.Flags = g_prop.Flags = b_prop.Flags = FREEHORIZ | AUTOKNOB; r_prop.HorizBody = g_prop.HorizBody = b_prop.HorizBody = 0x1000; r_prop.HorizPot = g_prop.HorizPot = b_prop.HorizPot = 0x8000; FREEHORIZ - Set this flag if you only want the "slider" or "Pot" to slide horizontally. FREEVERT - Set this flag if you want to slide vertically only. PROPBORDERLESS - Set this flag if you don't want borders. KNOBHIT - Check this flag if you want to know if the mouse button is on the knob. For example, if you want a standard slider which is horizontally placed in the window, set the flags: AUTOKNOB | FREEHORIZ If you are using a custom gadget, for instance a "joystick" that can move BOTH horizontally and vertically like the source code example in this lesson. then set the flags: FREEHORIZ | FREEVERT. -- (HorizPot) - Horizontal Position value for the gadget. -- (VertPot) - Vert Position value for the gadget. -- (horizBody) - The incremental width of the gadget. If set to 0x1000, means that the pot will move 1/16th of the full position if the mouse was pressed between the slider and the side of the gadget. it always moves "towards" the point where the mouse was pressed. The "joystick" custom gadget in the example sort of "Follows" the mouse. -- (VertBody) - Vertical incremental width of the gadget. The following fields are set by Intuition and are accessable to the user if needed. -- (cWidth) - Container width. -- (cHeight) - Container height. -- (HPotRes, VPotRes) - Horiz and Vert increments. -- (LeftBorder, TopBorder) - Container borders. Here is a PropInfo structure for a custom designed joystick. struct PropInfo cust_prop = { FREEHORIZ | FREEVERT, /* Want knob to go both vert and horiz */ 0x8000, 0x8000, /* Want knob to be centered initially */ 0x800, 0x800, /* Smallest increment the knob can move */ 150, 50, /* cWidth, cHeight - Container w & h */ 1, 1, /* HPosres, VPotres - Pot increments */ 0, 0 /* Container borders */ }; You can stick this anywhere in the global declarations section of your program. Because of the limitations of the Lattice C compiler, It should be declared "Before" the Gadget structure that uses it. An important thing to remember about constructing gadgets is that the Coordinates in the Gadget structure are relative to the coordinates in the Window or Screen. The coordinates in the border, or intuitext structure used by gadgets are RELATIVE to the Gadgets and NOT the window. For instance, if you want text to be displayed OVER the gadget rectangle, your Y coords will be about -8 to -10, while your x coords will be positive. The point where text is written is at the UPPER LEFT section of the letters and NOT the lower left portion of the letters like in the Macintosh. If you use borders around your gadgets, make sure you make room for at least one pixel around the "Selection box". Because, when the user presses in the box, it is inversed, and if the border coincides with a pixel in the selection box, that pixel overlaps and turns into another color, thus making it look a bit cludgey. HOW TO CREATE GADGETS IN YOUR PROGRAM ------------------------------------- FIRST OFF - Sketch out an approximate picture showing approximately what the overall layout of the gadgets should look like. Eventually, we will write a Gadget Editor, but for now, lets make one up the old fashion way. Define gadget structures for each gadget. Set the gadget flags appropriately, specify pointers to Special structures, like "PropInfo" or "StringInfo" structure in the "SpecialInfo" field. If you have custom images that you have created, you need to declare a pointer to the image. When you declare the gadgets, you create the LAST gadget first in your Source code. Each sucessive gadget then "Links" to the one in front of it. The last gadget pointer is a NULL. After each of the gadgets are linked properly, then: Declare any custom images, borders, or Text associated with the gadget. This section of code shows an example of this. Remember, this portion of code should come BEFORE any of the gadget declarations because the C compiler doesn't seem to be able to forward reference. /* Image for a custom proportional gadget */ UWORD custimage[] = { 0x0000, 0x0000, 0x0180, 0x0660, 0x1818, 0x2004, 0x4002, 0x4002, 0x4002, 0x4002, 0x2004, 0x1818, 0x0660, 0x0180, 0x0000, 0x0000 }; struct Image cus_image = { 0, 0, /* LeftEdge, TopEdge */ 16, 16, 1, /* Width, Height, Depth */ &custimage[0], /* Pointer to bit image */ 1, 0, /* PlanePick, Planeonoff */ NULL /* No other images */ }; One of the gadgets that use the above image: #define CUST_KNOB 4 struct Gadget cust_knob = { &tex_gad, 17, 140, 150, 50, GADGHCOMP, GADGIMMEDIATE | RELVERIFY, PROPGADGET, (APTR)&cus_image, NL, &cus2_txt, NL, (APTR)&cust_prop, CUST_KNOB, NL }; Now, we have to make sure the very first gadget is linked to our window OR SCREEN. In this particular example, we are only using a window, and are using the Intuition WorkBench Screen. The NewWindow structure is declared further down in the Code as shown below. Making the assumption that the gadget shown above is the FIRST gadget (last in listing), then the New Window structure is shown as below. /*************************************************************************** * N E W W I N D O W S T R U C T U R E ***************************************************************************/ struct NewWindow nw = { 0, 0, /* Start position */ 320, 200, /* width, height, */ 0, 1, /* detail, block pens */ CLOSEWINDOW /* IDCMP flags */ | REFRESHWINDOW | MOUSEBUTTONS | MOUSEMOVE | GADGETDOWN <-- MUST SET THIS ONE | GADGETUP, <-- AND THIS ONE /* Regular flags for gadgets and such */ WINDOWDEPTH | WINDOWSIZING | WINDOWDRAG | REPORTMOUSE <-- AND THIS ONE | WINDOWCLOSE | SMART_REFRESH, &cust_knob, /* First gadget in list <-- WE DO IT HERE */ NULL, /* User checkmark */ "Fun with Gadgets", /* Window Title */ NULL, /* Pointer to screen (Set later) */ NULL, /* Pointer to superbitmap */ 0, 0, 320, 186, /* Ignored because not sizeable */ WBENCHSCREEN, /* Using the Workbench screen */ }; The example program given uses a different First Gadget. The last thing you should do is set the appropriate flags. That is shown above. RESPONDING TO THOSE GADGETS --------------------------- Last but not least, you need to be able to respond, read, or act on a gadget. This is done by "Listening" to a gadget port. A particular bit comes to you in a Message class field. You must recieve this "message" from Intuition telling you that the user "clicked" the mouse in a particular gadget. You don't know which one yet. But you snatch that "message" by detecting a certain bit in the message.class field. You tuck this value away for later use and "Reply" to the message, using the ReplyMsg function. We keep doing this over and over until the user closes the window. This is called an "Event loop". /*************************************************************************** MAIN EVENT LOOP ***************************************************************************/ for (;;) { if (message = (struct IntuiMessage *)GetMsg(w->UserPort)) { MessageClass = message->Class; code = message->Code; ReplyMsg(message); switch (MessageClass) { case GADGETUP : case GADGETDOWN : do_gadgets(message, w); break; case CLOSEWINDOW : close_things(); exit(0); break; case MOUSEBUTTONS: break; } /* Case */ } /* if */ } /* for */ } /* main */ In the Message, contains the Address of the gadget chosen by the user. You pass the Message and the Window into a function called do_gadgets. This function identifies the gadget, then acts on it in the appropriate way. This function is listed below. I basically grab the address of the particular structure, then look for that special private code that I described to identify the gadget. /*************************************************************************** HANDLE THE GADGETS ***************************************************************************/ do_gadgets (mes, win) struct IntuiMessage *mes; /* Pointer to Message structure */ struct Window *win; /* And a window structure */ { struct Gadget *igad; /* Ptr to gadget that Intuition found */ int gadgid; /* ID Code identifying which gadget */ ULONG val; igad = (struct Gadget *) mes->IAddress; /* Ptr to a gadget */ gadgid = igad->GadgetID; /* My own personal code for this gad */ val = (ULONG)TexString.LongInt; switch(gadgid) { case GREEN_GADGET: break; case BLUE_GADGET : break; case TEX_GAD : printf("got here ...\n"); printf("val = %ld\n", val); break; } } As each gadget is identified, a CASE statement selects the appropriate action to be taken depending on the gadget. In many cases, the SAME action is usually performed on similar gadgets, but in the above example, I only handle 3 gadgets. I could have exampled more, but Good ol Lattice C has a nasty bug that won't let me use four cases without crashing. This is why it takes so long to write programs on the Amiga. But thats the price for being a pioneer. Feel free to chop it up and hack it to death. So gotta work on Menus tommorrow. Only have the Amiga for 5 more days. Next on my aggenda for learning is: Menus Requestors Sound This is it for now. Have fun hacking, and I will be back with more later. Feel free to ask me questions, and tell me your problems with gadgets so we can solve them.
dale@amiga.UUCP (Dale Luck) (12/13/85)
In article <338@well.UUCP> crunch@well.UUCP (John Draper) writes: >/*************************************************************************** > MAIN EVENT LOOP >***************************************************************************/ > > for (;;) > { > > if (message = (struct IntuiMessage *)GetMsg(w->UserPort)) { > MessageClass = message->Class; > code = message->Code; > ReplyMsg(message); > switch (MessageClass) { > } /* Case */ > } /* if */ > } /* for */ >} /* main */ > This is similar to the loop I had in the dot.c program If your program does need to do anything while waiting for messages though you should use for (;;) { Wait(1<<w->UserPort->mp_SigBit); /* wake up when message there */ if (message = (...... etc. } This will be much more fair to those other programs and to intuition as well.
dale@amiga.UUCP (Dale Luck) (12/13/85)
Sorry for posting this again but I forgot and important NOT In article <394@amiga.amiga.UUCP> dale@tooter.UUCP (Dale Luck) writes: > This is similar to the loop I had in the dot.c program > If your program does NOT need to do anything while waiting for > messages though you should use In general busy waiting is not fair in the multitasking amiga. The schedular does not change your priority since this was meant to be a realtime multitasker. This means programs must cooperate a little.