| Home | zeGraph lib | Lua lib | Custom lib | Tutorials | Notes | XML Script | C-Talk | Z-Script |
Lesson 1
Lesson 2
Lesson 3
Lesson 4
Lesson 5
Lesson 6
Lesson 7
Lesson 8
Lesson 9
Lesson 10
Lesson 11
Lesson 12
Lesson 13
Lesson 14
Lesson 15
Lesson 16
Lesson 17
Lesson 18
Lesson 19

Lesson 19 Window and User Interface

The Window package has several functions for creating window and controls. The wrapping of Windows system functions by zeGraph is straitforward: it simply send Windows callback message and parameters to the Lua call back function of yours. A Window callback function has the form of

WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

In which the first argument is the Window's handle, the second is the message ID, and the third and the fourth are parameters whose contents vary depending on the message. In some cases, parameter contents of wParam and lParam are stored in their lower-order and higher-order bytes.

The Lua callback function that you supply should deal with eight parameters with the first being the message as string, the second as the Window's handle, the third to fifth as the wParam and its lower- and higher-order bytes, and the sixth to eigth as lParam and its lower- and higher-order bytes. The following Window's ID are send back as string:

WM_CREATE: 			"CREATE"
WM_COMMAND:			"COMMAND"
WM_SIZE:			"SIZE"
WM_PAINT:			"PAINT"
WM_KEYUP:			"KEYUP"
WM_KEYDOWN:			"KEYDOW"
WM_LBUTTONUP:		"LBUTTONUP"
WM_LBUTTONDOWN:		"LBUTTONDOWN"
WM_LBUTTONDBLCLK:	"LBUTTONDBLCLK"
WM_MOUSEMOVE:		"MOUSEMOVE"
WM_RBUTTONUP:		"RBUTTONUP"
WM_RBUTTONDOWN:		"RBUTTONDOWN"
WM_RBUTTONDBLCLK:	"RBUTTONDBLCLK"
WM_TIMER:			"TIMER"

The first time you use the window function, you may wonder when you get what message; and which parameters or bytes contain the code of the key you pressed or the mouse's position moving over the clieant area of the window created by you. To find out these information, you can simply write a callback function to print all the parameters:

require("register")

callback = function(message, hwnd, wparm, lwparm, hwparm, lparm, llparm, hlparm)
    print(message, hwnd, wparm, lwparm, hwparm, lparm, llparm, hlparm)
end

win = zeWindow.create("callback", 200, 200)

Any window created zeGraph, including the main window, can not be resized by dragging its corner. But you can achieve resizing through the callback function. ZeGraph's main purpose of wrapping Windows sytem functions is to create simple user interface. Not having to deal with window resizing simplifies laying out controls in the main window. For advanced window programming by Lua, you should use wxLua or Lua-FLTK; and advanced window should resume to C or C++ for good performance.

Controls in the main Window should be create only once. Because the user cannot resize the window, the best way is to create controls is in handling the WM_SIZE message. You may attemp to do that in the WM_CREATE message loop. It will not work. In addition to commonly used control buttons, you can also create a pop menu for the main window. Let's see how to do these thing:

require("register")

callback = function(message, hwnd, wparm, lwparm, hwparm, lparm, llparm, hlparm)
    if (message == "RBUTTONUP") then
        zeWindow.showmenu(llparm, hlparm)
    elseif (message == "CREATE") then
        hmain = hwnd;        
    elseif (message == "SIZE") then
        zeWindow.button(hmain, 50, 50, 70, 30, "OK")
        zeWindow.combobox(hmain, 200, 50, 80, 400, "Item1", "Item2",
                                                           "Item3", "Item4", "Item5")
        zeWindow.menu("menu1", "menu2", "menu3", "menu4", "menu5")
    end
end

zeWindow.create("callback", 400, 300)

Creating a message window or a dialog windows for choosing color or file does not require the main window and the callback function:

require("register")
zeWindow.message("Hi! this if for testing the message box.\nThe is the second line.")

require("register")
print(zeWindow.choosecolor())
--[[A output:
0  64  128
]]

Now let's see how to do animation. The ring of Sauron in the Lord of Ring has magical powers. So does zeGraph. In just a few lines of code, you can create a ring that looks real enough to fool a hobbit. And yet you can rotate and animate the ring by creating a window. Watch this.

require("register")

render, scene, root, node, light, material
    = zeGrf.new("render", "scene", "node", "node", "light", "material")
render:set{color = {0, 0, 0, 1}}
render:add(scene)
scene:perspective()
scene:set{node = root}
root:add(light, material, node)
light:set{position = {100, 500, 100}, ambient = {0.3, 0.3, 0.3, 1}}

material:set{ambient = {0.24725, 0.2245, 0.0645, 1, 0},
             diffuse = {0.34615, 0.3143, 0.0903, 1, 0},
             specular = {0.797357, 0.723991, 0.208006, 1, 0},
             shininess = {83.2, 0}}

--Create the ring

arr = zeUtl.new("double")

R = 100; r = 15

for k = 0, 350, 10 do
    shape, xyz, nor = zeGrf.new("polygon", "vertex", "vertex")
    shape:set{vertex = xyz, vertex_normal = nor, type = "quadstrip"}
    node:add(shape)
    zeMake.toroid(arr, R, r, k, 10, 32)
    xyz:add(arr)
    arr:shift(3)
    nor:add(arr)
end

node:set{color = {1, 1, 1, 1}}

--Define the callback function before creating the window and define

hwnd = 0
rotx = -60
roty = 0
key = 0
deg = 2
tm = 30

node:rotatex(rotx)

callback = function(message, hwnd, wparm, lwparm, hwparm, lparm, llparm, hlparm)
    if (message == "PAINT") then
        -- Window asks to repaint it.
        if (hwnd > 0) then
            render:towindow(hwnd, 1)  
        end
    elseif (message == "KEYUP") then
        -- Key up.
        if (wparm >= 37 and wparm <= 40) then
            if (wparm == 37) then
                -- left arrow
                roty = roty - deg
            elseif (wparm == 38) then
                -- up arrow
                rotx = rotx - deg
            elseif (wparm == 39) then
                -- right arrow
                roty = roty + deg
            elseif (wparm == 40) then
                -- down arrow
                rotx = rotx + deg
            end

            key = wparm
            node:rotatex(rotx)
            node:rotatey(roty)
            render:towindow(hwnd, 1)  
        end
        
    elseif (message == "LBUTTONDBLCLK") then
        --Double click left mouse
        zeWindow.timer(tm)
        if (tm > 0) then
            tm = 0
        else
            tm = 30
        end
        
    elseif (message == "TIMER") then
        -- Timer. Animate scene.
        if (key == 37) then
            roty = roty - deg
        elseif (key == 38) then
            rotx = rotx - deg
        elseif (key == 39) then
            roty = roty + deg
        elseif (key == 40) then
            rotx = rotx + deg
        end

        node:rotatex(rotx)
        node:rotatey(roty)
        render:towindow(hwnd, 1)  

    elseif (message == "CREATE") then
        -- Window created. Save its handle.
        hwnd = wparm
    end
    
end

zeWindow.create("callback", 512, 512)

You rotate the ring using the four arrow keys. Double clicking the left mouse button will trigger the timer, which will animate (rotating) the ring. Another double click will kill the timer and stop the animation.