Re: +Help!
- Posted by David Cuny <dcuny at DSS.CA.GOV> May 08, 1998
- 1353 views
Luis wrote: > Can someone explain me with accuracy and simplicity how can i > make 'clickeable' by the mouse a certain area of a text > mode screen? In graphics or text mode, it's pretty simple. The process is called "hit = testing". Whenever there's a mouse event, you have to check to see if = it's hit anything, typically by checking if it fell within some = boundary. For example, the text "Quit" at {10,10} falls within a bounds rectangle = of {{10,10},{14,10}}, because the text is 4 characters across (10+4-1) = and 1 character high (10+1-1). Notice I subtracted 1 from both values, = because we are using {1,1} as the offset of our coordinates, instead of = {0,0}. The point falls into the bounding rectangle if it is greater or equal to = the upper-left hand corner, and less or equal to the lower-right hand = corner. It looks something like: if mouse_click() then -- simplified -- greater or equal to the upper left corner if mouse_x >=3D 10 and mouse_y >=3D 10 -- less or equal to the lower right corner and mouse_x <=3D 14 and mouse_y <=3D 10 then -- the user pressed "Quit" end if =20 end if You could write this as a routine: function in_bounds( sequence mouse, sequence p1, sequence p2 ) -- returns true if mouse inside rectangle defined by p1, p2 return -- returns true if all tests are true mouse[1] >=3D p1[1] -- upper left x and mouse[2] >=3D p1[2] -- upper left y and mouse[1] <=3D p2[1] -- lower right x and mouse[2] <=3D p2[2] -- lower right y end function =20 Here's a call to the function: if mouse_click() then if in_bounds( mousePos, {10,10}, {10,14} ) then -- "quit" clicked end if end if For handling several buttons on the screen, you could save the buttons = in a sequence, such as: constant Owner =3D 1, Title =3D 2, P1 =3D 3, P2 =3D 4 sequence buttons =3D { { 1, "Quit", {10, 10}, {14, 10} }, { 1, "Help", {16, 10}, {20, 10} } } =20 The routine would then look at each button to see if it had been = pressed: =20 -- mouse click? if mouse_click() then -- look at each button button =3D "" for i =3D 1 to length( buttons ) do -- was this button hit by the mouse? if in_bounds( mousePos, button[i][P1], button[i][p2] ) then -- save the button name button =3D button[i][Title] -- leave the loop exit end if end for end if If you wanted to add windows to your application, it would be a little = bit more complex, but not much. Because windows can overlap, you need to = test the closest window first: +----------------+ | window 2 | | | | +--------------+ | | window 1 | | | | | | | | X | | +----|-----------+ | | | +--------------+ As you can see (if you are looking at this with a fixed-width font), the = "X" is a point shared by both windows. Which window does it belong to? = The answer is: whichever window is tested first: +----------------+ | window 2 | | | | +--------------+ | | window 1 | | | | | | X | +----| | | | +--------------+ If you test window 1 first (because it's the first window), then it gets = the mouse hit. The order of the windows is called the "z order". The "z" = refers to the third dimension (depth).=20 When you draw the windows, you draw them in reverse z order, so the = first windows ends up on top of all the rest. Once you know which window is hit, you then do the button test on each = button in the window to see which button, if any, was pressed. So the = logic looks something like: if the mouse is pressed, then check windows in z order for a hit if a window was hit, then if that window wasn't on top, then put it on top else if a button in the window was hit then do that action end if end if end if end if That's more or less what the inner loop of a windowing system looks = like. I've included a simple windowing program at the end of this = e-mail. There are a number of differences in the code: - The second window point is a relative size, not a screen point - The button position is relative to the window's {2,2} position - The button size is calculated on the text size A major bit that is missing here is the clipping routines. When you = write into a window, any text that doesn't fit should be clipped, such = as: +----------------+ | In a real windo| | | | | +----------------+ instead of this: +----------------+ | In a real window, this wouldn't look like this. | | | | +----------------+ But I needed to leave *something* for you to do. Hope this helps! -- David Cuny -- HERE'S THE CODE! -- textwin.e -- simple text windows include mouse.e -- attributes constant Owner =3D 1, Title =3D 2, P1 =3D 3, P2 =3D 4 sequence buttons, windows, z_order -- define some buttons buttons =3D { { 1, "< Quit >", {1, 1} }, { 2, "< Help >", {1, 1} } } -- define some windows windows =3D { { 1, "Window 1", {3, 3}, {17, 17} }, { 1, "Window 2", {10, 10}, {40, 15} } } -- the drawing order of the windows z_order =3D { 1, 2 } =20 integer key, clicked sequence mouse_pos object mouse function remove( object o, sequence s ) -- remove all instances of o from s sequence s2 =20 =20 -- empty list s2 =3D {} =20 =20 -- go though list for i =3D 1 to length( s ) do -- if NOT a match if compare( s[i], o ) then -- add to list s2 =3D append( s2, s[i] ) end if end for =20 =20 return s2 =20 end function procedure put_on_top( integer window ) -- place window first in z order =20 =20 -- remove from list z_order =3D remove( window, z_order ) =20 -- place first z_order =3D prepend( z_order, window ) =20 end procedure procedure draw_button( integer button ) -- draw button in owner's window =20 integer owner sequence at, ownerAt -- get button's position at =3D buttons[button][P1] -- get owner owner =3D buttons[button][Owner] -- get owner's position ownerAt =3D windows[owner][P1] =20 -- add to owner's position, and adjust at =3D at + ownerAt =20 -- position position( at[2], at[1] )=20 =20 -- text puts( 1, buttons[button][Title] ) end procedure procedure draw_buttons( integer owner ) -- draw buttons in windows -- buttons are relative to the owner's position =20 -- look though all the buttons =20 for i =3D 1 to length( buttons ) do -- owned by this window? if buttons[i][Owner] =3D owner then -- draw the button =20 draw_button( i ) end if =20 end for end procedure procedure draw_rect( integer x1, integer y1,=20 integer cx, integer cy ) -- draw top position( y1, x1 ) puts( 1, "+" & repeat( '-', cx-2 ) & "+" ) -- draw middle for y =3D y1+1 to y1+cy-2 do position( y, x1 ) puts( 1, "|" & repeat( ' ', cx-2 ) & "|" ) end for =20 =20 -- draw bottom position( y1+cy-1, x1 ) puts( 1, "+" & repeat( '-', cx-2 ) & "+" ) =20 end procedure =20 procedure draw_window( integer window ) -- draw button in owner's window integer x, y, cx, cy -- get position x =3D windows[window][P1][1] y =3D windows[window][P1][2] cx =3D windows[window][P2][1] cy =3D windows[window][P2][2] -- draw a rectangle draw_rect( x, y, cx, cy ) =20 -- title position( y, x+2 ) =20 puts( 1, " " & windows[window][Title] & " " ) -- draw the buttons draw_buttons( window ) end procedure procedure draw_windows() =20 -- draw in reverse z order for i =3D length( z_order ) to 1 by -1 do draw_window( z_order[i] ) end for end procedure function in_rect( integer x, integer y,=20 integer x1, integer y1,=20 integer x2, integer y2 ) -- true if point is in rectangle return x >=3D x1 and y >=3D y1 and x <=3D x2 and y <=3D y2 =20 end function =20 function hit_window( integer x, integer y, integer window ) -- true if point is in window =20 integer x1, y1, x2, y2 =20 =20 -- window point x1 =3D windows[window][P1][1] y1 =3D windows[window][P1][2] x2 =3D x1 + windows[window][P2][1] y2 =3D y1 + windows[window][P2][2] =20 return in_rect( x, y, x1, y1, x2, y2 ) =20 end function =20 function hit_button( integer x, integer y, integer button ) -- true if point is in button integer x1, y1, x2, y2, owner =20 -- get owner owner =3D buttons[button][Owner] =20 -- button point x1 =3D buttons[button][P1][1] y1 =3D buttons[button][P1][2] =20 -- add owner x1 =3D x1 + windows[owner][P1][1] -1 y1 =3D y1 + windows[owner][P1][2] -1 =20 -- calculate size x2 =3D x1 + length( buttons[button][Title] ) - 1 y2 =3D y1 =20 return in_rect( x, y, x1, y1, x2, y2 ) =20 end function integer clicked_window, clicked_button -- draw all the controls draw_windows() -- event loop while 1 do =20 -- was the escape key pressed? if get_key() =3D 27 then exit end if -- look for mouse event mouse =3D get_mouse() -- event? if sequence( mouse ) then -- mouse press? clicked =3D and_bits( mouse[1], LEFT_DOWN ) -- adjust mouse[2] =3D mouse[2]/8 mouse[3] =3D mouse[3]/8 else -- no click clicked =3D 0 end if -- if clicked... =20 if clicked then =20 -- clear variables clicked_window =3D 0 clicked_button =3D 0 =20 -- go through windows in z order for i =3D 1 to length( z_order ) do -- hit? if hit_window( mouse[2], mouse[3], z_order[i] ) then -- save window clicked_window =3D z_order[i] =20 -- exit loop exit end if end for =20 -- hit window? =20 if clicked_window then =20 -- is it on top? if z_order[1] =3D clicked_window then -- check buttons for i =3D 1 to length( buttons ) do -- owned by window? if buttons[i][Owner] =3D clicked_window then -- hit button? if hit_button( mouse[2], mouse[3], i ) then -- save button clicked_button =3D i -- exit exit = =20 end if end if end for =20 else =20 -- put window on top put_on_top( clicked_window ) -- redraw windows draw_windows() end if end if -- display results position( 1, 1 ) if clicked_window then if clicked_button then -- put up button title printf( 1, "hit button %s",=20 {buttons[clicked_button][Title]} ) =20 else -- put up window title printf( 1, "hit window %s",=20 {windows[clicked_window][Title]} ) end if end if =20 end if =20 end while