Gem, which stands for the ``Graphics and Events Manager'', can be used independently of most of the rest of the Amulet. Gem uses the wrapper mechanism (for styles and fonts), but it does not use the ORE object system.
gem.h
. Gem also uses types.h
for wrappers, gdefs.h
for styles, images, point lists, and fonts, and idefs.h
for input events. For a complete description of Amulet include files and how to use them, see Section 1.8 in the Overview chapter.
9.3 Drawonables
The primary data structure in Gem is the Am_Drawonable
, which is a C++ object that corresponds to a window-manager window or off-screen buffer (for example, in X11 it corresponds to a ``drawable''). We called it a ``drawonable'' because it is something that you can draw on. We also wanted to reserve the word ``window'' for the Opal level object that corresponds to the drawonable. In this manual, sometimes ``window'' is used for ``drawonable'' since drawonables are implemented as window-manager windows.9.3.1 Creating Drawonables
Programmers create a root drawonable at initialization, and then create other drawonables as children of the root (or as children of another drawonable). The typical initialization is:Am_Drawonable *root = Am_Drawonable::Get_Root_Drawonable();
At the Opal level, this is called automatically by Am_Initialize
to set up the exported Am_Screen
object. Under X11, Get_Root_Drawonable
takes an optional string parameter which is the name of the screen. You can call therefore call Get_Root_Drawonable
multiple times to support multiple screens.Create
method. If you use root.Create
you get a top-level window, and if you use another drawonable, then it creates a sub-window. All of the parameters of the create call are optional, and are:
int l = 0
: the left of the new window in the coordinates of its parent.
int t = 0
: the top of the window
unsigned int w = 100
: width of the window
unsigned int h = 100
: height
const char* tit = ""
: the title for the window
const char* icon_tit = ""
: the string to display with the icon for the window.
bool vis = true
: whether the window is initially visible on the screen on not.
bool initially_iconified = false
: whether the window starts out as an icon.
Am_Style back_color = Am_No_Style
: the initial color for the background of the window.
bool save_under_flag = false
: save the bitmaps underneath the window (useful for pop-up menus).
int min_w = 1
: The minimum size allowed for this window (when the user or program resizes it). You can't have 0
size windows.
int min_h = 1
: Minimum height.
int max_w = 0
: The maximum width allowed for the window. 0
is illegal so means no maximum.
int max_h = 0
: Maximum height.
bool title_bar_flag = true
: Whether the title line is displayed or not. Under X11 having no title line means the window is not managed by the window manager.
bool query_user_for_position = false
: If true
, then the initial left and top are ignored and the user is queried instead.
bool query_user_for_size = false
: If true
, then the initial width and height are ignored and the user is queried instead.
bool clip_by_children_flag = true
: If false
, then graphics drawn on the window show through all children windows (drawonables) created as children of this window.
Am_Input_Event_Handlers *evh = NULL) = 0
: How input is handled for this window, see Section 9.5.1.
Am_Drawonable* Create_Offscreen (
int width = 0, int height = 0,
Am_Style background_color = Am_No_Style)To destroy a drawonable call the
Destroy
method, which destroys the drawonable and all its children, including removing them from the screen:
void Destroy ()
Am_Drawonable* Am_Drawonable::Narrow (Am_Ptr ptr
): given an arbitrary pointer, this casts it to be a Am_Drawonable
. Because drawonables are not wrappers, no checking is done.
void Reparent (Am_Drawonable *new_parent)
bool Inquire_Window_Borders(int& left_border, int& top_border, int& right_border, int& bottom_border, int& outer_left, int& outer_top)
: return the current window border sizes. For X11, this may be inaccurate for windows that are not yet visible. Returns true
if the return values are meaningful, false
otherwise.
void Raise_Window (Am_Drawonable *target_d)
: Move the window to the top of all its siblings in Z order. If target_d
is NULL
, then the window is moved so it is not covered by any other windows. If target_d
is a valid Am_Drawonable, then the window is put above target_d
in Z order.
void Lower_Window (Am_Drawonable *target_d)
: Move the window to the bottom of all its siblings (if target_d
is NULL
), or just until it is below target_d
.
void Set_Iconify (bool iconified)
: Make the window be iconified or not iconified.
void Set_Title (const char* new_title)
: Change window title.
void Set_Icon_Title (const char* new_title)
: Change icon title.
void Set_Position (int new_left, int new_top)
: Move the drawonable.
void Set_Size (unsigned int new_width, unsigned int new_height)
: Change the size.
void Set_Max_Size (unsigned int new_width, unsigned int new_height)
: Change the maximum size.
void Set_Min_Size (unsigned int new_width, unsigned int new_height)
: Change the minimum size.
void Set_Visible (bool vis)
: Make the window visible or not.
void Set_Titlebar (bool new_title_bar)
: Set whether has a title bar.
void Set_Background_Color (Am_Style new_color)
bool Get_Iconify ()
const char* Get_Title ()
const char* Get_Icon_Title ()
void Get_Position (int& l, int& t)
: Sets l and t to current left and top.
void Get_Size (int& w, int& h)
void Get_Max_Size (int& w, int& h)
void Get_Min_Size (int& w, int& h)
bool Get_Visible ()
void Get_Titlebar (bool& title_bar_flag)
Am_Style& Get_Background_Color ()
int Get_Depth ()
: Returns the current pixel depth in bits (e.g. 8
for 8-bit color).
bool Is_Color ()
: Returns true
if the window is color or false
if not.
void Get_Values (int& l, int& t, int& w, int& h, const char*& tit, const char*& icon_tit, bool& vis, bool& iconified_now, Am_Style& back_color, bool& save_under_flag, int& min_w, int& min_h, int& max_w, int& max_h, bool& title_bar_flag, bool& clip_by_children_flag, int& bit_depth)
: returns all of the parameters at once.
The actual pixels drawn in a drawonable by the various drawing routines are described in detail in the Opal chapter of the Amulet manual. The Opal objects' slots are passed as parameters to the Gem level drawing routines. The
Am_Style, Am_Image_Array
, and Am_Font
structures are also described there. That information will not be repeated here.
void Beep()
: Causes a sound on the machine this drawonable is displayed on. This is called by the Opal-level Am_Beep()
routine.
void Set_Cursor (Am_Cursor cursor)
: Set the cursor for the drawonable. The mouse pointer will display the specified cursor whenever the mouse is within the bounds of this drawonable, and it will display the default cursor whenever the mouse exits a drawonable with a specific cursor. Amulet does not support setting a cursor globally, so that it has a custom image no matter where the mouse is pointing.
virtual void Bitblt (int d_left, int d_top, int width, int height, Am_Drawonable* source, int s_left, int s_top, Am_Draw_Function df = Am_DRAW_COPY)
: Bitblt
copies a rectangular area from one drawonable to another, using the specified drawing function. The destination for Bitblt
is the Am_Drawonable
this message is sent to.
void Clear_Area (int left, int top, int width, int height)
: This clears a rectangular region in the drawonable to the drawonable's background style.
void Flush_Output()
: This causes all pending output to be displayed on the screen. Under X11, you will not see any graphics until this is called, unless you're running an event loop. Calling this routine is not strictly necessary on the PC or Mac, but it is needed to maintain machine independency.
void Translate_Coordinates (int src_x, int src_y, Am_Drawonable *src_d, int& dest_x_return, int& dest_y_return)
: Convert the coordinates in one window to be coordinates in another window. To translate to or from screen coordinates, pass the root drawonable as the source or destination drawonable. Translating coordinates from a child of one root drawonable to a child of another root causes an Am_Error()
. If the source or destination drawonable are offscreen drawonables, an Am_Error()
will result.
bool Test_Image (const Am_Image_Array& image)
: Verifies that image
is loaded (or loadable).
void Get_Image_Size (const Am_Image_Array& image, int& ret_width, int& ret_height)
: Sets ret_width
and ret_height
with
the width and height of the specified image array as if it were displayed on this drawonable.
int Get_Char_Width (const Am_Font& Am_font, char c)
: Returns the width in pixels of character c
as if displayed in the font Am_font
on this drawonable.
int Get_String_Width (const Am_Font& Am_font, const char* the_string, int the_string_length):
Returns the width, in pixels, of the first the_string_length
characters of the_string
, as if it were displayed in font Am_font
on this drawonable
void Get_String_Extents (const Am_Font& Am_font, const char* the_string, int the_string_length, int& width, int& ascent, int& descent, int& left_bearing, int& right_bearing):
This returns the extents of the first the_string_length
characters of the_string
, as if drawn in font Am_font
on this drawonable. The total height of the bounding rectangle for this string is ascent + descent
. left_bearing
is the distance from the origin of the text to the first inked pixel. The right_bearing
is the distance from the origin of the text to the last inked pixel.
void Get_Font_Properties (const Am_Font& Am_font, int& max_char_width, int& min_char_width, int& max_char_ascent, int& max_char_descent):
The max ascent
and max_descent
include vertical spacing between rows of text. The max_char_width
and min_char_width
are the width, in pixels, of the widest and narrowest characters in Am_font
on this drawonable.
There is only one clip mask per root drawonable. All drawonables which have the same root share the same clip region. It is necessary to set the clip region for each window before drawing the contents of that window. For efficiency, it's a good idea to complete drawing in one window before resetting the clip region and drawing in other windows.
The clip region can be set with a rectangular region, or with an arbitrarily shaped
Am_Region
. Regions are described in Section 9.4.4. The root drawonable stores a stack of clipping regions. Use Push_Clip()
and Pop_Clip()
to push regions onto this stack, or to pop them off. The current clip region is the intersection of all of the regions currently on the stack.
void Clear_Clip():
Clear the clip region and empty the clip region stack: no clipping will occur.
void Set_Clip (Am_Region* region):
This empties the clip region stack and sets the clipping region to the specified Am_Region.
void Set_Clip (int left, int top, unsigned int width, unsigned int height):
This empties the clip region stack and sets the clipping region to the rectangular region specified by left
, top
, width
, and height.
void Push_Clip (Am_Region* region):
This pushes the specified Am_Region
onto the drawonable's clip region stack.
void Push_Clip (int left, int top, unsigned int width, unsigned int height)
: This pushes the rectangular region specified by left
, top
, width
, and height
onto the drawonable's clip region stack.
void Pop_Clip ()
: This pops the most recently pushed region off of the drawonable's clip region stack. Popping when there's nothing on the stack is silently ignored.
In_Clip()
routines provide a way to ask a drawonable if a point is inside its clip region. When asking if a given region is inside the drawonable's clip region, you can use the total
parameter to determine whether the given region is completely inside the clip region, or whether it just intersects it.
bool In_Clip (int x, int y)
: Returns true
if the point (x, y)
is inside this drawonable's clip region, and false
otherwise.
bool In_Clip (int left, int top, unsigned int width, unsigned int height, bool &total)
: Returns true
if the rectangular region specified by left
, top
, width
, and height
intersects this drawonable's clip region. total
is set to true
if the clip region completely contains the rectangle, and false
if part of the rectangle is outside the clip region.
bool In_Clip (Am_Region *rgn, bool &total)
: Returns true
if rgn
intersects this drawonable's clip region. total
is set to true
if the clip region completely contains rgn
, and false
if part of it is outside the clip region.
Am_Region
class describe arbitrarily shaped regions. Am_Region
is a generalization of a drawonable's clip region, discussed in Section 9.4.3. By using the member functions listed below, you can build a region of arbitrary shape to install as the clip region of a drawonable.
class Am_Region {
public:
static Am_Region* Create ();
virtual void Destroy () = 0;
virtual void Clear () = 0;
virtual void Set (int left, int top, unsigned int width,
unsigned int height) = 0;
virtual void Push (Am_Region* region) = 0;
virtual void Push (int left, int top, unsigned int width,
unsigned int height) = 0;
virtual void Pop () = 0;
virtual void Union (int left, int top, unsigned int width,
unsigned int height) = 0;
virtual void Intersect (int left, int top, unsigned int width,
unsigned int height) = 0;
virtual bool In (int x, int y) = 0;
virtual bool In (int left, int top, unsigned int width,
unsigned int height, bool &total) = 0;
virtual bool In (Am_Region *rgn, bool &total) = 0;
};
9.4.5 Drawing Functions
All of the drawing functions take a Am_Draw_Function
parameter
which controls how the pixels of the drawn shape affect the screen. Since most programmers will use color screens, draw functions are not usually useful, although they are used for special effects such as fade groups in Opal. The supported values for Am_Draw_Function
are:
Am_DRAW_COPY
Am_DRAW_OR
Am_DRAW_XOR
Am_DRAW_GRAPHIC_OR
Am_DRAW_GRAPHIC_XOR
Am_DRAW_GRAPHIC_AND
Am_DRAW_GRAPHIC_NIMP
Am_DRAW_MASK_COPY
Am_DRAW_OR
, etc.) compensate for black and white monitors which chose to make black 0
and white 1
. The GRAPHIC
versions provide unadulterated bitwise operations. The operation Am_DRAW_MASK_COPY
is a short cut operator for drawing masks. Be advised that not all of the drawing functions support all of the values of Am_Draw_Function
. In particular, Draw_Image
only supports Am_DRAW_COPY
and Am_DRAW_MASK_COPY
.The
Am_Style
objects Am_On_Bits
and Am_Off_Bits
are special colors for making masks. Am_On_Bits
has a color of all ones so that:
Am_On_Bits AND Some_Color == Some_Color
Am_On_Bits OR Some_Color == Am_On_Bits
And Am_Off_Bits
has a color of all zeros so that:
Am_OFF_Bits AND Some_Color == Some_Color
Am_Off_Bits OR Some_Color == Am_Off_Bits
All of the following drawing routines draw in either an onscreen or offscreen drawonable. The parameters ls
and fs
specify the line style and fill style for the drawing operations.
void Draw_Arc (const Am_Style& ls, const Am_Style& fs, int left, int top, unsigned int width, unsigned int height, int angle1 = 0, int angle2 = 360, Am_Draw_Function f = Am_DRAW_COPY, Am_Arc_Style_Flag asf = Am_ARC_PIE_SLICE)
: Draw an optionally filled arc. To draw a circle, let angle2
= 360.
void Draw_Image (int left, int top, int width, int height, const Am_Image_Array& image, int i_left = 0, int i_top = 0, const Am_Style& ls = Am_No_Style, const Am_Style& fs = Am_No_Style, bool draw_monochrome = false, Am_Draw_Function f = Am_DRAW_COPY)
: This draws the contents of an Am_Image_Array
onto this drawonable. For transparent images, fs
is used for the background color; if fs
is Am_No_Style
, the background pixels will be transparent. Otherwise, fs
and ls
are ignored, unless the draw_monochrome
parameter is true
. When draw_monochrome
is true
, the image is drawn using ls
for the foreground pixels, and fs
for the background pixels; if fs
is Am_No_Style
, the background pixels will be transparent; if ls
is Am_No_Style
, the foreground pixels will be transparent.
void Get_Polygon_Bounding_Box (const Am_Point_List& pl, const Am_Style& ls, int& out_left, int& out_top int& width, int& height)
: Calculates the bounding box of the specified polygon.
void Draw_Line (const Am_Style& ls, int x1, int y1, int x2, int y2, Am_Draw_Function f = Am_DRAW_COPY)
: Draws a single straight line with style ls
.
void Draw_Lines (const Am_Style& ls, const Am_Style& fs, const Am_Point_Array& pts, Am_Draw_Function f = Am_DRAW_COPY)
: Draws an optionally filled polygon.
void Draw_2_Lines (const Am_Style& ls, const Am_Style& fs, int x1, int y1, int x2, int y2, int x3, int y3, Am_Draw_Function f = Am_DRAW_COPY)
: This is provided for more efficient drawing of an optionally filled polygon with exactly 2 lines.
void Draw_3_Lines (const Am_Style& ls, const Am_Style& fs, int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, Am_Draw_Function f = Am_DRAW_COPY)
: This is provided for more efficient drawing of an optionally filled polygon with exactly 3 lines.
void Draw_Rectangle (const Am_Style& ls, const Am_Style& fs, int left, int top, int width, int height, Am_Draw_Function f = Am_DRAW_COPY)
: This draws an optionally filled rectangle.
void Draw_Roundtangle (const Am_Style& ls, const Am_Style& fs, int left, int top, int width, int height, unsigned short x_radius, unsigned short y_radius, Am_Draw_Function f = Am_DRAW_COPY)
: This draws a rectangle with rounded corners.
void Draw_Text (const Am_Style& ls, const char *s, int str_len, const Am_Font& Am_font, int left, int top, Am_Draw_Function f = Am_DRAW_COPY, const Am_Style& fs = Am_No_Style, bool invert = false)
: This draws a single line of text in the specified font. ls
specifies the style of the text, and fs
specifies the style of the rectangular region behind the text. If fs
is Am_No_Style
, the background is transparent.
Am_Input_Event_Handlers
. You can specify one instance of this class for each of the drawonables in your application. The event handler methods take the drawonable and event information as parameters. For example, each time the user hits a keyboard key over a certain window, that window's event handlers are retrieved, and, the
Input_Event_Notify
member function is called, with information about what key was pressed, which drawonable received the keypress, and where the mouse was positioned within the window when the key was pressed.Opal defines standard event handlers, so anyone programming at the Opal layer or above will not have to provide these event handlers. Instead, you should use an interactor for event handling, or create a new interactor if none of the available interactors is appropriate. To add or change the functionality of the standard opal event handlers, derive a new class from the C++ class
Am_Standard_Opal_Handlers
(exported in opal_advanced.h
), and replace some of the virtual functions with your own event handlers.
Am_Input_Event_Handlers
is defined as:
class Am_Input_Event_Handlers {
public:
virtual void Iconify_Notify (Am_Drawonable* draw, bool iconified) = 0;
virtual void Frame_Resize_Notify (Am_Drawonable* draw, int left,
int top, int right, int bottom) = 0;
virtual void Destroy_Notify (Am_Drawonable *draw) = 0;
virtual void Configure_Notify (Am_Drawonable *draw, int left, int top,
int width, int height) = 0;
virtual void Exposure_Notify (Am_Drawonable *draw,
int left, int top,
int width, int height) = 0;
virtual void Input_Event_Notify (Am_Drawonable *draw,
Am_Input_Event *ev)=0;
};The member functions are:
Iconify_Notify
: Called when the window is being iconified or de-iconified. The parameter iconified
is true
if the window is now iconified, and false
if it was just de-iconified.
Frame_Resize_Notify
: Called whenever the window's border size changes (for example, if title bars are added or removed).
Destroy_Notify
: Called when the user requests that the window be destroyed. Note that window managers often don't actually destroy the windows, but rather call this routine to tell the programs to destroy the window.
Configure_Notify
: Called whenever the user changes the window's size or position.
Exposure_Notify
: Called when the window becomes uncovered and part of it needs to be redrawn. The rectangle specified by left
, top
, width
, and height
is the bounding box of the region that should be redrawn.
Input_Event_Notify
: Called for all input events from the keyboard and mouse. The Am_Input_Event
is described below.
void Set_Input_Dispatch_Functions (Am_Input_Event_Handlers* evh)
void Get_Input_Dispatch_Functions (Am_Input_Event_Handlers*& evh)
Input_Event_Notify
method is a C++ class containing the position of the mouse when the event occurred, the drawonable of the event, a timestamp, and an Am_Input_Char
describing the event. Am_Input_Chars
are described in Chapter 5, Interactors and Command Objects for Handling Input.
class Am_Input_Event {
public:
Am_Input_Char input_char; //the char and modifier bits; see idefs.h
int x;
int y;
Am_Drawonable *draw; // Drawonable this event happened in
unsigned long time_stamp;
};
You can control which input events are generated for a drawonable using the following member functions of drawonables:
void Set_Enter_Leave (bool want_enter_leave_events()
: If you want Input_Event_Notify()
to be called when the mouse enters or leaves this drawonable, Set_Enter_Leave(true)
. The default is false
.
void Set_Want_Move (bool want_move_events):
If you want a window to receive move events, set this to true
. The default is false
.
void Set_Multi_Window (bool want_multi_window):
When an interactor should run over multiple windows, this method should be called on each window. Otherwise, the cursor is ``reserved'' for the original window the mouse is clicked in.
void Get_Window_Mask (bool& want_enter_leave_events, bool& want_move_events, bool& want_multi_window)
: This returns information about which input events a drawonable is receiving.
Am_Drawonable * Get_Drawonable_At_Cursor()
Am_Double_Click_Time
is the inter-click wait time in milliseconds. The default value is 250. If it is 0, then no double-click processing is done. On the Mac, Am_Double_Click_Time
is ignored. The global system click time is used instead. It is usually set from the Mouse Control Panel.
Am_Initialize
(which among other things, calls Get_Root_Drawonable
), then sets up a number of objects, and then calls Am_Main_Event_Loop (
Section 4.3.4)
or Am_Do_Events
(Section 4.3.5). These routines then call the Gem level routines where the events are actually processed.
extern bool Am_Main_Loop_Go;
class Am_Drawonable {
public:
...
static void Main_Loop ();
static void Process_Event (const Am_Time& timeout);
static void Process_Immediate_Event ();
static void Wait_For_Event();
...
Am_Main_Event_Loop
calls Process_Event;
Am_Do_Events
calls Process_Immadiate_Event
. A Gem-level programmer who wants to process events, but not use any of the higher-level Amulet operations like demons and Opal might use Am_Drawonable::Main_Loop
(). This just repeatedly calls Am_Drawonable::Process_Event
. To stop any of the main loops, you can set the exported bool
called Am_Main_Loop_Go
to false
.Process_Event
and Process_Immediate_Event
is that Process_Event
waits for the next event, and processes exactly one input event and all non-input events (like refresh and configure_notify events) before and after that input event before returning. For example: before after
xxxIyyyIzzz ---> Izzz
Process_Event
returns when it encounters a second input event, when the queue is empty or, if its timeout
parameter is non-zero, when the time specified has expired. The class Am_Time
is defined in amulet/include/gdefs.h,
and includes a constructor that converts an unsigned long
literal from milliseconds to an Am_Time
object (Section 6.3).
Process_Immediate_Event
does not wait for an event, but processes the first event in the queue and all non-input events after it until an input event is seen. Process_Immedi-ate_Event
returns when it encounters an input event (excluding the case where the first event is an input event) or when the queue is empty.Wait_for_Event
waits until the event queue is non-empty. It does no processing.9.6 Hints for Using Gem Directly
If you are using Gem to make a custom kind of graphical object that you will use with the higher level parts of Amulet, then it may be prudent to create an instance of Am_Rectangle
and just override its draw method with whatever you need. Then the draw method will always be called at the appropriate times.Configure_Notify
or Exposure_Notify
event is processed. Any drawing you do before the window is mapped is lost. In the code that follows, the call to Process_Event
forces the Configure_Notify
event to be processed, which in turn causes the window to get mapped, and allows the following Draw_Rectangle to succeed.#include <amulet.h>
Am_Style red (1.0f, 0.0f, 0.0f);
Am_Style purple (1.0f, 0.0f, 1.0f);
int main (void)
{
Am_Drawonable *root = Am_Drawonable::Get_Root_Drawonable();
Am_Drawonable *win = root->Create(30, 50, 400, 400);
win->Process_Event (0UL); // force the window to be mapped
win->Draw_Rectangle(red, purple, 30, 40, 50, 50);
win->Flush_Output();
// to prevent the program from exiting too soon
printf("Hit RETURN to exit:");
getchar();
win->Destroy();
return 0;
}