1. +Help!
Hi everybody!
Can someone explain me with accuracy and simplicity how can i
make 'clickeable' by the mouse a certain area of a text-
mode screen?
Imagine that i have the text 'Quit' at position (10,10).I've
tried to figure out Jiri Babor's textmenu.e but it's quite
complex for me!
see ya!:)
- Luis -
(no more dosshell!i have already installed the cool-looking Norton
Commander)
2. Re: +Help!
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
3. Re: +Help!
Greetings,
> Can someone explain me with accuracy and simplicity how can i
> make 'clickeable' by the mouse a certain area of a text-
> mode screen?
> Imagine that i have the text 'Quit' at position (10,10).I've
> tried to figure out Jiri Babor's textmenu.e but it's quite
> complex for me!
>
>see ya!:)
> - Luis -
I've never tried it on a text screen before but this should work.
Ok, you know the get_mouse command? well you use that like this I think:
-- untested code --
include mouse.e -- of course
sequence qbutton,mouse
qbutton = "Quit"
position(10,10)
puts(1,qbutton)
mouse = get_mouse()
if mouse[2]>10 then
if mouse[3]>{10} then
if mouse[2]<10+length(qbutton) then
-- ^
--the [2] and [3] might need to be
--switched on these two lines to work
-- v
if mouse[3]<10 then
if find(LEFT_UP,mouse[1]) then
run_quit_routine() --you get the idea?
end if
end if
end if
end if
end if
-- end untested code --
I hope this isn't too complicated and in fact there is
probably a much simpler way to do it, I just haven't
figured it out yet: you might use find() more and compare()
but I'm not sure. I waited to see if anyone else would
answer your question before I did.
I didn't test this code but it should be close to working,
I just wish it didn't take so many statements.
Good luck,
Lewis Townsend
| ____ _ _ _ _ __
|\ | __/ || / | // || / __ \
| \ ||_ || //|| // || ||__\|
| \ | _| || // || // || \___ \
| | \ ||__ ||// ||// || |\__||
| |\ \|___\ |_/ |_/ || \____/
| | \ \ _____ ________
| | \ \ | __ \ | __ __ |
| | \ \ ||__|| |/ || \|
| | \ \ | __ / ||
| |_____\ \ || \\ ||
|__________\ || || ||
Keroltarr at hotmail.com
______________________________________________________
Get Your Private, Free Email at http://www.hotmail.com
4. Re: +Help!
In the windows test example code David Cuny wrote in the event loop of
the program:
-- look for mouse event
mouse = get_mouse()
-- event?
if sequence( mouse ) then
-- mouse press?
clicked = and_bits( mouse[1], LEFT_DOWN )
-- adjust
mouse[2] = mouse[2]/8
mouse[3] = mouse[3]/8
else
-- no click
clicked = 0
end if
I get a bug reported when running the program about a type mismatch with
mouse[2] when the procedure hit_window is called. I was able to
circumvent the problem by "flooring" the results for mouse[2] and
mouse[3]. The changes to the above line result in:
-- look for mouse event
mouse = get_mouse()
-- event?
if sequence( mouse ) then
-- mouse press?
clicked = and_bits( mouse[1], LEFT_DOWN )
-- adjust
mouse[2] = floor(mouse[2]/8)
mouse[3] = floor(mouse[3]/8)
else
-- no click
clicked = 0
end if
I am very new to euphoria so I do not know if this is the best solution,
but I found it let the program run without fault.
Steve Ranta
5. Re: +Help!
Steve Ranta suggested the fix:
>> mouse[2] =3D floor(mouse[2]/8)
>> mouse[3] =3D floor(mouse[3]/8)
Sorry about that. I hadn't gotten that result when I tested it. I looked =
at the code in my editor, but hadn't notice that the floor() function =
was applied later. You can also write:
mouse[2..3] =3D floor(mouse[2..3]/8)
I tend to forget you can do that sort of thing with sequences.
Thanks for the fix!
-- David Cuny
6. Re: +Help!
> Data de tramesa: Fri, 08 May 1998 15:03:36 -0500 (CDT)
> De : Lewis Townsend <keroltarr at HOTMAIL.COM>
> Assumpte: Re: +Help!
> A: Multiple recipients of list EUPHORIA
<EUPHORIA at MIAMIU.ACS.MUOHIO.EDU>
> Enviar resposta a: Euphoria Programming for MS-DOS
<EUPHORIA at MIAMIU.ACS.MUOHIO.EDU>
> Greetings,
>
> > Can someone explain me with accuracy and simplicity how can i
> > make 'clickeable' by the mouse a certain area of a text-
> > mode screen?
> > Imagine that i have the text 'Quit' at position (10,10).I've
> > tried to figure out Jiri Babor's textmenu.e but it's quite
> > complex for me!
> >
> >see ya!:)
> > - Luis -
>
> I've never tried it on a text screen before but this should work.
> Ok, you know the get_mouse command? well you use that like this I think:
>
> -- untested code --
>
> include mouse.e -- of course
> sequence qbutton,mouse
> qbutton = "Quit"
> position(10,10)
> puts(1,qbutton)
> mouse = get_mouse()
> if mouse[2]>10 then
> if mouse[3]>{10} then
> if mouse[2]<10+length(qbutton) then
> -- ^
> --the [2] and [3] might need to be
> --switched on these two lines to work
> -- v
> if mouse[3]<10 then
> if find(LEFT_UP,mouse[1]) then
> run_quit_routine() --you get the idea?
> end if
> end if
> end if
> end if
> end if
>
> -- end untested code --
>
> I hope this isn't too complicated and in fact there is
> probably a much simpler way to do it, I just haven't
> figured it out yet: you might use find() more and compare()
> but I'm not sure. I waited to see if anyone else would
> answer your question before I did.
>
> I didn't test this code but it should be close to working,
> I just wish it didn't take so many statements.
>
> Good luck,
>
> Lewis Townsend
> | ____ _ _ _ _ __
> |\ | __/ || / | // || / __ \
> | \ ||_ || //|| // || ||__\|
> | \ | _| || // || // || \___ \
> | | \ ||__ ||// ||// || |\__||
> | |\ \|___\ |_/ |_/ || \____/
> | | \ \ _____ ________
> | | \ \ | __ \ | __ __ |
> | | \ \ ||__|| |/ || \|
> | | \ \ | __ / ||
> | |_____\ \ || \\ ||
> |__________\ || || ||
> Keroltarr at hotmail.com
>
> ______________________________________________________
> Get Your Private, Free Email at http://www.hotmail.com
Hi Lewis,
I tested your code and it doesn't works.There's a type-check failure
that i think that i've remove it changing 'mouse' to a object.
Please,check out the following code! ;)
-- tested code --
include mouse.e -- of course
with trace
-- trace(1)
sequence qbutton
object mouse
integer clicked
mouse_events(LEFT_DOWN + LEFT_UP + RIGHT_DOWN)
----> we dont need the mouse to report the MOVE event i suppose!
qbutton = "Quit"
position(10,10)
puts(1,qbutton)
while 1 do
mouse = get_mouse()
--if mouse != -1 then ----> error!
---------------------------------------------------------------------
if sequence( mouse ) then -----> if not,mouse = -1 would cause error!
-- if sequence( mouse ) then
-- mouse press?
clicked = and_bits( mouse[1], LEFT_DOWN ) ----->???????
-- some one knows the use of and_bits() in here????????
-- adjust
mouse[2] = mouse[2]/8 --------> to scale the coordinates!
mouse[3] = mouse[3]/8 --------> Thanks David Cuny,I missed
-- this error!:)
if mouse[2]>10 and mouse[2]<15 and mouse[3] = 10 then -->it should
-- be the
-- coordinates
-- of the button
puts(1,"quiting...")
exit
else
position(1,1)
puts(1,"you must hit Quit!")
end if
else
-- no click
clicked = 0
-- if mouse[3]>{10} then
-- if mouse[2]<10+length(qbutton) then
-- ^
--the [2] and [3] might need to be
--switched on these two lines to work
-- v
-- if mouse[3]<10 then
-- if find(LEFT_UP,mouse[1]) then
-- run_quit_routine() --you get the idea?
-- end if
-- end if
-- end if
--end if
end if
mouse = {0,0,0} ---> to make sure that will be a sequence
end while
-- Thanks to David Cuny,Lewis Townsend and everybody!
- Luis -
>
7. Re: +Help!
(I think) Luis Campos wrote:
> -- mouse press?
> clicked =3D and_bits( mouse[1], LEFT_DOWN ) ----->???????
> -- some one knows the use of and_bits() in here????????
The 'and_bits' is needed because there can be more than one mouse event =
at a time. For example, the mouse could move AND the left button could =
be clicked. The flags are ORd together. In this case, the returned =
result would be:
MOVE AND LEFT_DOWN
which equals=20
1 AND 2
which is, of course, 3. In this case, the test:
if mouse[1] =3D LEFT_DOWN
would not detect the mouse click event, because 2 !=3D 3. If you look at =
the flags, they are set up in powers of 2:
global constant
MOVE =3D 1,
LEFT_DOWN =3D 2,
LEFT_UP =3D 4,
RIGHT_DOWN =3D 8
RIGHT_UP =3D 16,
MIDDLE_DOWN =3D 32,
MIDDLE_UP =3D 64
This is not by accident - you store all the flags into a single byte, by =
setting the _bits_ in the flag:
1 =3D bit 0 =3D 2^0
2 =3D bit 1 =3D 2^1
4 =3D bit 2 =3D 2^2
8 =3D bit 3 =3D 2^3
16 =3D bit 4 =3D 2^4
32 =3D bit 5 =3D 2^5
64 =3D bit 6 =3D 2^6
To look at a _single_ bit in the byte, you AND the bits together. So the =
test:
if and_bits( mouse[1], LEFT_DOWN )
works because
3 AND 1
is equal to 1, and a non-zero result is treated as true.
Note that 'and_bits' is different than 'and'; 'and' returns true if both =
values are non-zero. It works like this:
function and( object o1, object o2 )
if o1 then
if o2 then
return 1
end if
end if
return 0
end function
So the test:
-- WRONG!
if mouse[1] and LEFT_DOWN then
tests to see if both values are non-zero. It doesn't do actually do a =
bit-by-bit comparison of the values.
Hope this helps!
-- David Cuny
8. Re: +Help!
- Posted by David Cuny <dcuny at LANSET.COM>
May 13, 1998
-
Last edited May 14, 1998
I had mistakenly written:
>The flags are ORd together. In this case, the returned result would be:
>
> MOVE AND LEFT_DOWN
>
>which equals
>
> 1 AND 2
>
>which is, of course, 3.
I should have written:
>The flags are ORd together. In this case, the returned result would be:
>
> MOVE OR LEFT_DOWN => or_bits( MOVE, LEFT_DOWN )
>
>which equals
>
> 1 OR 2 => or_bits( 1, 2 )
>
>which is, of course, 3.
because 'and_bits( 1, 2 )' equals 0.
Sorry about the typo.
-- David Cuny
9. Re: +Help!
Salutations,
>Hi Lewis,
> I tested your code and it doesn't works.There's a type-check failure
> that i think that i've remove it changing 'mouse' to a object.
>
> Please,check out the following code! ;)
I'll be surprised if a type-check failure was all that was wrong with my
code. I used to call everything an object so that I wouldn't have to
worry about types much but I've been trying to be more specific
recently. Maybe it's better that way. (of course mouse needs to be an
object) I guess debugging type check errors is easier than trying to
figure out why your program is trying to subscript an atom (reading from
it) ;)
>-- Thanks to David Cuny,Lewis Townsend and everybody!
I'm flattered that you actually used some of my code. I figured that
some one else would have a much better solution.
Sincerely,
Lewis Townsend
|\ ____ _ _ _ _ __
| \ | __/ || / | // || / __ \
| \ ||_ || //|| // || ||__\|
| \ | _| || // || // || \___ \
| |\ \ ||__ ||// ||// || |\__||
| | \ \|___\ |_/ |_/ || \____/
| | \ \ _____ ________
| | \ \ | __ \ | __ __ |
| | \ \ ||__|| |/ || \|
| | \ \ | __ / ||
| |______\ \ || \\ ||
|___________\ || || ||
Keroltarr at hotmail.com
______________________________________________________
Get Your Private, Free Email at http://www.hotmail.com