Custom Buttons
Author |
Message |
Clayton
|
Posted: Sat Dec 09, 2006 10:40 am Post subject: Custom Buttons |
|
|
I was asked by a friend the other day why he couldn't pass procedures with parameters when he created a button in Turing. I told him quite plainly that Turing's built in GUI sucked. So he asked me how to get around it. I told him to make one himself, he claimed he didn't know how, so I did. Heres the source with an example program:
Turing: |
class Buttons
import Mouse
export Pressed, Create, CreateFull, Refresh, EnableRollover
var x, y, width, height : int
var text : string
var rollover_enabled : boolean := false
var rollover_color : int
const font := Font.New ("times new roman:12")
%Mouse Variables to keep track of whether the button is being pressed or not
var oldbutton : int := 0
procedure Create (x_, y_ : int, text_ : string)
x := x_
y := y_
text := text_
width := Font.Width (text, font ) + 10
height := 16
Draw.Box (x, y, x + width, y + height, black)
Draw.Fill (x + 1, y + 1, grey, black)
Font.Draw (text, x + (width div 2) - (Font.Width (text, font ) div 2), y + (height div 2 - 4), font, black)
end Create
procedure CreateFull (x_, y_, width_, height_ : int, text_ : string)
text := text_
x := x_
y := y_
width := width_
height := height_
Draw.Box (x, y, x + width, y + height, black)
Draw.Fill (x + 1, y + 1, grey, black)
Font.Draw (text, x + (width div 2) - (Font.Width (text, font ) div 2), y + (height div 2 - 4), font, black)
end CreateFull
fcn MouseHover : boolean
var mx, my, mb : int
Mouse.Where (mx, my, mb )
if mx >= x and my >= y and mx <= x + width and my <= y + height then
result true
else
result false
end if
end MouseHover
procedure Rollover
Draw.Box (x, y, x + width, y + height, black)
Draw.Fill (x + 1, y + 1, rollover_color, black)
Font.Draw (text, x + (width div 2) - (Font.Width (text, font ) div 2), y + (height div 2 - 4), font, black)
end Rollover
procedure EnableRollover (choice : boolean, clr : int)
rollover_color := clr
rollover_enabled := choice
end EnableRollover
fcn Pressed : boolean
var mx, my, mb : int
Mouse.Where (mx, my, mb )
if MouseHover and mb >= 1 and oldbutton = 0 then
oldbutton := 1
result false
elsif MouseHover and mb = 0 and oldbutton = 1 then
oldbutton := 0
result true
else
result false
end if
result false
end Pressed
procedure Refresh
if rollover_enabled and MouseHover then
Rollover
else
Draw.Box (x, y, x + width, y + height, black)
Draw.Fill (x + 1, y + 1, grey, black)
Font.Draw (text, x + (width div 2) - (Font.Width (text, font ) div 2), y + (height div 2 - 4), font, black)
end if
end Refresh
end Buttons
View.Set ("graphics, offscreenonly")
var button1 : ^Buttons
var button2 : ^Buttons
new Buttons, button1
new Buttons, button2
button1 -> CreateFull (maxx div 2, maxy div 2, 300, 100, "Hello World Happy Gilmore")
button2 -> Create (5, 5, "Button 2")
button1 -> EnableRollover (true, brightred)
button2 -> EnableRollover (true, purple)
loop
if button1 -> Pressed then
Draw.FillBox (0, 0, maxx, maxy, brightred)
View.Update
Time.Delay (1000)
end if
if button2 -> Pressed then
Draw.FillBox (0, 0, maxx, maxy, purple)
View.Update
Time.Delay (1000)
end if
button1 -> Refresh
button2 -> Refresh
View.Update
Draw.Cls
exit when hasch
end loop
|
a couple of procedures you should know about when using this:
Create (x, y : int, text : string)
Creates a button at (x,y) with text inside of it. The button will automatically become the width and height required for the text.
CreateFull (x, y, width, height : int, text : string)
Like Create, except you specify its width and height.
Pressed : boolean
Pressed simply returns true if the button has been pressed (ie the mouse button was released over it)
Refresh
Simply redraws the button on the screen again
EnableRollover (choice : boolean, Color : int)
Enables a rollover effect on the button (true for it to happen, false for it not to). With rollover enabled, the button will change color to Color when the mouse is over the button
Have fun with it, just remember to give credit to me if you use it.
NOTE: I will be making this a module later on so that more people will be able to use it.
EDIT: Any additions you would like to see are welcome! I'm always open to suggestions |
|
|
|
|
|
Sponsor Sponsor
|
|
|
Cervantes
|
Posted: Sat Dec 09, 2006 11:02 am Post subject: Re: Custom Buttons |
|
|
Freakman wrote: I was asked by a friend the other day why he couldn't pass procedures with parameters when he created a button in Turing. I told him quite plainly that Turing's built in GUI sucked.
I'd just like to point out that there is a reason this can't be done. It's because each button is an object, and one of it's instance variables is an action procedure. That is, a procedure with no parameters. Turing is a typed language, and a procedure with one parameter has a different type than a procedure with no parameters. In the class for the button, the instance variable must be given a type. It's got to be either a procedure with no parameters, or a procedure with one parameter, or.... Thus, all buttons have to be consistant with each other. The best way to do this is to make all buttons use action procedures.
This wouldn't be much of a problem if Turing had support for anonymous functions. |
|
|
|
|
|
Clayton
|
Posted: Sat Dec 09, 2006 11:04 am Post subject: (No subject) |
|
|
Aye, I know that. I was just explaining that too him (also didn't want to drone on with my post [Which I think I did anyway]). Any other suggestions though Cervantes? |
|
|
|
|
|
Cervantes
|
Posted: Sat Dec 09, 2006 11:14 am Post subject: (No subject) |
|
|
You've handled the buttons' actions the same way I did. I'm actually not so sure that's the best way to do it, anymore. It means having a really big main loop, because you've got to do all those if statements yourself. Can you find a way to make both approaches possible?
I'd also suggest giving the buttons some visual charactersitc that shows when they are pressed down. The button should look different when the mouse is pressing it than when the mouse is merely hovering over it.
In your Create procedure, you assigned a value to `height'. Why not assign that variable outside that procedure, right when you declare the variable? The code inside a class is run each time an object is created, and `self' is that object, so doing it this way would give you the same effect. It's not necessarily better, though. Just something to consider.
Here's something else to consider. Your code checks the state of the mouse each time the Pressed function is called. You'll be checking the state of the mouse once for each button you've got. That's not really necessary. We should only have to check the state of the mouse at most once each time through the loop. So, one possibility is to use a global variable for the state of the mouse, but we all hate global variables. Another possibility is to pass the state of the mouse into the Pressed function as a parameter. This could also give you the power to sort of fudge the position of the mouse if you wanted to, by passing in bogus values.
To show off the fact that your buttons don't act like the action-procedure slaves that Turing's GUI's buttons are, why don't you make a procedure that takes in a colour as a parameter and does your little "draw the screen that colour, View.Update, then delay(1000)"? |
|
|
|
|
|
Clayton
|
Posted: Sat Dec 09, 2006 11:28 am Post subject: (No subject) |
|
|
Thanks for the suggestions Cervantes, I'll be sure to try and get those points worked into there somehow. For now though, I have decided to get everything moved into a module (the class still exists, but the programmer doesn't deal directly with it). The modules' name has become Buttons for simplicity. the class name is now buttons instead. The code: (with the procedure Cervantes mentioned above)
Turing: |
class buttons
import Mouse
export Pressed, Create, CreateFull, Refresh, EnableRollover
var x, y, width, height : int
var text : string
var rollover_enabled : boolean := false
var rollover_color : int
const font := Font.New ("times new roman:12")
%Mouse Variables to keep track of whether the button is being pressed or not
var oldbutton : int := 0
procedure Create (x_, y_ : int, text_ : string)
x := x_
y := y_
text := text_
width := Font.Width (text, font ) + 10
height := 16
Draw.Box (x, y, x + width, y + height, black)
Draw.Fill (x + 1, y + 1, grey, black)
Font.Draw (text, x + (width div 2) - (Font.Width (text, font ) div 2), y + (height div 2 - 4), font, black)
end Create
procedure CreateFull (x_, y_, width_, height_ : int, text_ : string)
text := text_
x := x_
y := y_
width := width_
height := height_
Draw.Box (x, y, x + width, y + height, black)
Draw.Fill (x + 1, y + 1, grey, black)
Font.Draw (text, x + (width div 2) - (Font.Width (text, font ) div 2), y + (height div 2 - 4), font, black)
end CreateFull
fcn MouseHover : boolean
var mx, my, mb : int
Mouse.Where (mx, my, mb )
if mx >= x and my >= y and mx <= x + width and my <= y + height then
result true
else
result false
end if
end MouseHover
procedure Rollover
Draw.Box (x, y, x + width, y + height, black)
Draw.Fill (x + 1, y + 1, rollover_color, black)
Font.Draw (text, x + (width div 2) - (Font.Width (text, font ) div 2), y + (height div 2 - 4), font, black)
end Rollover
procedure EnableRollover (choice : boolean, clr : int)
rollover_color := clr
rollover_enabled := choice
end EnableRollover
fcn Pressed : boolean
var mx, my, mb : int
Mouse.Where (mx, my, mb )
if MouseHover and mb >= 1 and oldbutton = 0 then
oldbutton := 1
result false
elsif MouseHover and mb = 0 and oldbutton = 1 then
oldbutton := 0
result true
else
result false
end if
result false
end Pressed
procedure Refresh
if rollover_enabled and MouseHover then
Rollover
else
Draw.Box (x, y, x + width, y + height, black)
Draw.Fill (x + 1, y + 1, grey, black)
Font.Draw (text, x + (width div 2) - (Font.Width (text, font ) div 2), y + (height div 2 - 4), font, black)
end if
end Refresh
end buttons
%Actual module to use to make buttons
module Buttons
import buttons
export Create, CreateFull, Refresh, EnableRollover, Pressed
var button_stack : flexible array 1 .. 0 of ^buttons
fcn Create (x, y : int, text : string) : int
new button_stack, upper (button_stack ) + 1
new buttons, button_stack (upper (button_stack ))
button_stack (upper (button_stack )) -> Create (x, y, text )
result upper (button_stack )
end Create
fcn CreateFull (x, y, width, height : int, text : string) : int
new button_stack, upper (button_stack ) + 1
new buttons, button_stack (upper (button_stack ))
button_stack (upper (button_stack )) -> CreateFull (x, y, width, height, text )
result upper (button_stack )
end CreateFull
procedure Refresh (button_id : int)
button_stack (button_id ) -> Refresh
end Refresh
procedure EnableRollover (button_id : int, choice : boolean, clr : int)
button_stack (button_id ) -> EnableRollover (choice, clr )
end EnableRollover
fcn Pressed (button_id : int) : boolean
result button_stack (button_id ) -> Pressed
end Pressed
end Buttons
%Example Program
procedure fill_screen (clr : int)
Draw.FillBox (0, 0, maxx, maxy, clr )
View.Update
Time.Delay (1000)
end fill_screen
View.Set ("graphics, offscreenonly")
var button1 : int := Buttons.CreateFull (maxx div 2, maxy div 2, 300, 100, "Hello World Happy Gilmore")
var button2 : int := Buttons.Create (5, 5, "Button 2")
Buttons.EnableRollover (button1, true, brightred)
Buttons.EnableRollover (button2, true, purple)
loop
if Buttons.Pressed (button1 ) then
fill_screen (brightred)
end if
if Buttons.Pressed (button2 ) then
fill_screen (purple)
end if
Buttons.Refresh (button1 )
Buttons.Refresh (button2 )
View.Update
Draw.Cls
exit when hasch
end loop
|
Because the class is now being handled through a module, I had to find a way to get my procedures and functions to act upon seperate buttons (as opposed to all of them). So now Create and CreateFull are functions returning ints. This is your button id. The procedures breakdown like this:
Create (x, y : int, text : string) : int
Same as before, it just returns the buttons new id.
CreateFull (x, y, width, height : int, text : string) : int
Again, only thing that is different is that it returns a button id.
Refresh (button_id : int)
You now have to pass it the button id that you want it to refresh.
EnableRollover (button_id : int, choice : boolean, clr : int)
Just like the above, except you have to pass it the button id that you want to have the rollover.
Pressed (button_id : int) : boolean
Just pass it the button id now.
Any other suggestions, just say them. |
|
|
|
|
|
Hackmaster
|
Posted: Sat Dec 09, 2006 3:56 pm Post subject: (No subject) |
|
|
I would just like to point out that there is another way around the first stated problem, altough it's ugly, and quite frankly, your way is much better. but, for people who are new, and don't know about objects, a very, very verboten way to do this is simply to use global variables in your procedure. (shudder)
it counts as an action proc now, because it dosen't take parameters... but all other coders will hate you. just putting that out there. |
|
|
|
|
|
joedirt
|
Posted: Sun Dec 17, 2006 7:19 pm Post subject: (No subject) |
|
|
hey freakman,
I'm a fairly new to turing a i was woundering if you could explain or make a code for a simple custon button. One where all you do is click and there is no rollover of whatever, just as simple as you can.
-thanks |
|
|
|
|
|
Clayton
|
Posted: Sun Dec 17, 2006 7:30 pm Post subject: (No subject) |
|
|
code: |
var button1 : int := Buttons.Create (30, 30, "Hello")
loop
if Buttons.Pressed (button1) then
Draw.FillBox (maxx div 2, maxy div 2, maxx, maxy, brightred)
Time.Delay(100)
end if
end loop
|
Read my above posts and it well tell you all you need to know to understand. |
|
|
|
|
|
Sponsor Sponsor
|
|
|
joedirt
|
Posted: Sun Dec 17, 2006 8:03 pm Post subject: (No subject) |
|
|
i got this far but i still can't figure out how to make a if statment which only works when the mouse is over a button and the mouse is clicked at the same time. in this code i'll click away from the button then move my mouse over it and it still works. Please help.
var x, y, button : int
procedure redbox
Draw.FillBox (10, 10, 50, 50, red)
end redbox
procedure box
Draw.FillBox (10, 10, 50, 50, black)
end box
loop
Mouse.Where (x, y, button)
box
var x1, y1 : int
if x < 50 and y < 50 and Mouse.ButtonMoved ("down") then
redbox
delay (1000)
end if
end loop |
|
|
|
|
|
ericfourfour
|
Posted: Sun Dec 17, 2006 11:28 pm Post subject: (No subject) |
|
|
I have two ideas to handle the actions. The first one is to use an action class. The second is to inherit the button object and use it. |
|
|
|
|
|
|
|