lazarus/components/opengl/usergui_example/imguimain.pas

551 lines
13 KiB
ObjectPascal

unit imguimain;
{$mode objfpc}{$H+}
interface
uses
Classes, Forms, Controls, Graphics, ExtCtrls, OpenGLContext, LazLoggerBase,
GL, GLExt, LCLType, fpImage, SysUtils;
type
{ TGLColor }
TGLColor = record
alpha: GLushort;
blue: GLushort;
green: GLushort;
red: GLushort;
end;
{ TForm1 }
TForm1 = class(TForm)
OpenGLControl1: TOpenGLControl;
Timer1: TTimer;
procedure FormCreate(Sender: TObject);
procedure FormKeyDown(Sender: TObject; var Key: word; Shift: TShiftState);
procedure FormKeyPress(Sender: TObject; var Key: char);
procedure OpenGLControl1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: integer);
procedure OpenGLControl1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: integer);
procedure OpenGLControl1MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: integer);
procedure OpenGLControl1Paint(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
private
{ private declarations }
first: Boolean;
public
{ public declarations }
end;
{ UIStateType }
UIStateType = object
activeitem: integer;
hotitem: integer;
kbditem: integer;
keychar: integer;
keyentered: integer;
keymod: TShiftState;
lastwidget: integer;
mousedown: integer;
mousex: integer;
mousey: integer;
procedure Init;
procedure SetMousePos(X, Y: integer);
end;
var
Form1: TForm1;
uistate: UIStateType;
implementation
uses
uglyfont;
var
bgcolor: integer;
sometext: string;
genid: integer;
{ UIStateType }
procedure UIStateType.Init;
begin
mousex := 0;
mousey := 0;
mousedown := 0;
hotitem := 0;
activeitem := 0;
kbditem := 0;
keyentered := 0;
keymod := [];
lastwidget := 0;
end;
procedure UIStateType.SetMousePos(X, Y: integer);
begin
mousex := X;
mousey := Y;
end;
{$R *.lfm}
function GLColor(color: GLuint): TGLColor;
begin
Result.red := (color shr 16) and $0000ff;
Result.green := (color shr 8) and $0000ff;
Result.blue := (color shr 0) and $0000ff;
//Result.alpha := (color shr 0) and $000000ff;
Result.alpha := $ff;
end;
//Draw the string. Characters are fixed width, so this is also
//deadly simple.
procedure drawstring(str: string; x, y: double);
begin
glTextOut(x, y + 14, 0, 14, 14, 1, 0, str);
end;
//Simplified interface to OpenGL's fillrect call
procedure drawrect(x, y, w, h: integer; AColor: TGLColor);
begin
glColor3ub(AColor.red, AColor.green, AColor.blue);
glRectf(x, y, x + w, y + h);
end;
//Check whether current mouse position is within a rectangle
function regionhit(x, y, w, h: integer): integer;
begin
if (uistate.mousex < x) or
(uistate.mousey < y) or
(uistate.mousex >= x + w) or
(uistate.mousey >= y + h) then
Result := 0
else
Result := 1;
end;
//Simple button IMGUI widget
function button(id: integer; x: integer; y: integer): integer;
begin
//Check whether the button should be hot
if regionhit(x, y, 64, 48) = 1 then
begin
uistate.hotitem := id;
if (uistate.activeitem = 0) and (uistate.mousedown = 1) then
uistate.activeitem := id;
end;
//If no widget has keyboard focus, take it
if uistate.kbditem = 0 then
uistate.kbditem := id;
//If we have keyboard focus, show it
if uistate.kbditem = id then
drawrect(x - 6, y - 6, 84, 68, GLColor($ff0000));
drawrect(x + 8, y + 8, 64, 48, GLColor($000000));
//Render button
if uistate.hotitem = id then
begin
if uistate.activeitem = id then
//Button is both 'hot' and 'active'
drawrect(x + 2, y + 2, 64, 48, GLColor($ffffff))
else
//Button is merely 'hot'
drawrect(x, y, 64, 48, GLColor($ffffff));
end
else
//button is not hot, but it may be active
drawrect(x, y, 64, 48, GLColor($aaaaaa));
//If we have keyboard focus, we'll need to process the keys
if uistate.kbditem = id then
begin
case uistate.keyentered of
VK_TAB:
begin
//If tab is pressed, lose keyboard focus.
//Next widget will grab the focus.
uistate.kbditem := 0;
//If shift was also pressed, we want to move focus
//to the previous widget instead.
if ssShift in uistate.keymod then
uistate.kbditem := uistate.lastwidget;
//Also clear the key so that next widget
//won't process it
uistate.keyentered := 0;
end;
VK_RETURN:
begin
//Had keyboard focus, received return,
//so we'll act as if we were clicked.
Result := 1;
exit;
end;
end;
end;
uistate.lastwidget := id;
//If button is hot and active, but mouse button is not
//down, the user must have clicked the button.
if (uistate.mousedown = 0) and
(uistate.hotitem = id) and
(uistate.activeitem = id) then
Result := 1
else
//Otherwise, no clicky.
Result := 0;
end;
//Simple scroll bar IMGUI widget
function slider(id: integer; x: integer; y: integer; max: integer; var Value: integer): integer;
var
ypos: integer;
mousepos: integer;
v: integer;
begin
//Calculate mouse cursor's relative y offset
ypos := ((256 - 16) * Value) div max;
//Check for hotness
if regionhit(x + 8, y + 8, 16, 255) = 1 then
begin
uistate.hotitem := id;
if (uistate.activeitem = 0) and (uistate.mousedown = 1) then
uistate.activeitem := id;
end;
//If no widget has keyboard focus, take it
if uistate.kbditem = 0 then
uistate.kbditem := id;
//If we have keyboard focus, show it
if uistate.kbditem = id then
drawrect(x - 4, y - 4, 40, 280, GLColor($ff0000));
drawrect(x, y, 32, 256 + 16, GLColor($777777));
//Render the scrollbar
if (uistate.activeitem = id) or (uistate.hotitem = id) then
drawrect(x + 8, y + 8 + ypos, 16, 16, GLColor($ffffff))
else
drawrect(x + 8, y + 8 + ypos, 16, 16, GLColor($aaaaaa));
//If we have keyboard focus, we'll need to process the keys
if uistate.kbditem = id then
begin
case uistate.keyentered of
VK_TAB:
begin
//If tab is pressed, lose keyboard focus.
//Next widget will grab the focus.
uistate.kbditem := 0;
//If shift was also pressed, we want to move focus
//to the previous widget instead.
if ssShift in uistate.keymod then
uistate.kbditem := uistate.lastwidget;
//Also clear the key so that next widget
//won't process it
uistate.keyentered := 0;
end;
VK_UP:
begin
//Slide slider up (if not at zero)
if Value > 0 then
begin
Dec(Value);
Result := 1;
exit;
end;
end;
VK_DOWN:
begin
//Slide slider down (if not at max)
if Value < max then
begin
Inc(Value);
Result := 1;
exit;
end;
end;
end;
end;
uistate.lastwidget := id;
//Update widget value
if uistate.activeitem = id then
begin
mousepos := uistate.mousey - (y + 8);
if mousepos < 0 then
mousepos := 0;
if mousepos > 255 then
mousepos := 255;
v := (mousepos * max) div 255;
if v <> Value then
begin
Value := v;
Result := 1;
exit;
end;
end;
Result := 0;
end;
function GetTickCount: DWord;
begin
Result := DWord(Trunc(Now * 24 * 60 * 60 * 1000));
end;
function textfield(id: integer; x: integer; y: integer; var buffer: string): integer;
var
len: integer;
changed: integer;
begin
len := Length(buffer);
changed := 0;
//Check for hotness
if regionhit(x - 4, y - 4, 30 * 14 + 8, 24 + 8) = 1 then
begin
uistate.hotitem := id;
if (uistate.activeitem = 0) and (uistate.mousedown = 1) then
uistate.activeitem := id;
end;
//If no widget has keyboard focus, take it
if uistate.kbditem = 0 then
uistate.kbditem := id;
//If we have keyboard focus, show it
if uistate.kbditem = id then
drawrect(x - 6, y - 6, 30 * 14 + 12, 24 + 12, GLColor($ff0000));
//Render the text field
if (uistate.activeitem = id) or (uistate.hotitem = id) then
drawrect(x - 4, y - 4, 30 * 14 + 8, 24 + 8, GLColor($aaaaaa))
else
drawrect(x - 4, y - 4, 30 * 14 + 8, 24 + 8, GLColor($777777));
glColor3ub($00, $00, $00);
drawstring(buffer, x, y);
//Render cursor if we have keyboard focus
if (uistate.kbditem = id) and (((GetTickCount shr 8) and 1) = 1) then
drawstring('_', x + len * 14, y);
//If we have keyboard focus, we'll need to process the keys
if uistate.kbditem = id then
begin
case uistate.keyentered of
VK_TAB:
begin
//If tab is pressed, lose keyboard focus.
//Next widget will grab the focus.
uistate.kbditem := 0;
//If shift was also pressed, we want to move focus
//to the previous widget instead.
if ssShift in uistate.keymod then
uistate.kbditem := uistate.lastwidget;
uistate.keyentered := 0;
end;
VK_BACK:
begin
//Also clear the key so that next widget
//won't process it
if len > 0 then
begin
Delete(buffer, len, 1);
Dec(len);
changed := 1;
end;
end;
end;
if (uistate.keychar >= 32) and (uistate.keychar < 127) and (len < 30) then
begin
buffer := buffer + Chr(uistate.keyentered);
Inc(len);
changed := 1;
end;
end;
//If button is hot and active, but mouse button is not
//down, the user must have clicked the widget; give it
//keyboard focus.
if (uistate.mousedown = 0) and (uistate.hotitem = id) and (uistate.activeitem = id) then
uistate.kbditem := id;
uistate.lastwidget := id;
Result := changed;
end;
procedure imgui_prepare;
begin
uistate.hotitem := 0;
genid := 0;
end;
procedure imgui_finish;
begin
if uistate.mousedown = 0 then
uistate.activeitem := 0
else
if uistate.activeitem = 0 then
uistate.activeitem := -1;
//If no widget grabbed tab, clear focus
if uistate.keyentered = VK_TAB then
uistate.kbditem := 0;
//Clear the entered key
uistate.keyentered := 0;
uistate.keychar := 0;
end;
function GEN_ID: integer;
begin
Inc(genid);
Result := genid;
end;
//Rendering function
procedure render;
var
slidervalue: integer;
begin
imgui_prepare;
button(GEN_ID, 50, 50);
button(GEN_ID, 150, 50);
if button(GEN_ID, 50, 150) = 1 then
bgcolor := Round(Random * $ffffff);
if button(GEN_ID, 150, 150) = 1 then
halt(0);
textfield(GEN_ID, 50, 250, sometext);
slidervalue := bgcolor and $ff;
if slider(GEN_ID, 500, 40, 255, slidervalue) = 1 then
bgcolor := (bgcolor and $ffff00) or slidervalue;
slidervalue := ((bgcolor shr 10) and $3f);
if slider(GEN_ID, 550, 40, 63, slidervalue) = 1 then
bgcolor := (bgcolor and $ff00ff) or (slidervalue shl 10);
slidervalue := ((bgcolor shr 20) and $f);
if slider(GEN_ID, 600, 40, 15, slidervalue) = 1 then
bgcolor := (bgcolor and $00ffff) or (slidervalue shl 20);
imgui_finish;
end;
{ TForm1 }
procedure TForm1.OpenGLControl1Paint(Sender: TObject);
var
samples, samplebuffers: integer;
begin
if first then begin
glGetIntegerv(GL_SAMPLE_BUFFERS,@samplebuffers);
glGetIntegerv(GL_SAMPLES,@samples);
DebugLn(['SampleBuffers: ',samplebuffers]);
Debugln(['Samples: ',samples]);
first:=false;
end;
//setup 2D projection
glMatrixMode(GL_PROJECTION);
glLoadIdentity;
glOrtho(0, OpenGLControl1.Width, OpenGLControl1.Height, 0, 0, 1);
glMatrixMode(GL_MODELVIEW);
//clear screen
drawrect(0, 0, 640, 480, GLColor(bgcolor));
render;
glColor3ub($ff, $00, $00);
glTextOut(10, 20, 0, 10, 10, 1, 0, Format('%f FPS', [1000 / OpenGLControl1.FrameDiffTimeInMSecs]));
OpenGLControl1.SwapBuffers;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
OpenGLControl1.Paint;
end;
procedure TForm1.OpenGLControl1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: integer);
begin
uistate.SetMousePos(X, Y);
end;
procedure TForm1.OpenGLControl1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: integer);
begin
uistate.mousedown := 1;
end;
procedure TForm1.FormKeyDown(Sender: TObject; var Key: word; Shift: TShiftState);
begin
uistate.keymod := Shift;
uistate.keyentered := Key;
end;
procedure TForm1.FormKeyPress(Sender: TObject; var Key: char);
begin
//if escape is pressed, quit the application
if Ord(Key) = VK_ESCAPE then
halt(0);
uistate.keyentered := Ord(Key);
//if key is ASCII, accept it as character input
if (uistate.keyentered and $FF80) = 0 then
uistate.keychar := uistate.keyentered and $7f;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
bgcolor := $77;
sometext := 'Some text';
first:=true;
end;
procedure TForm1.OpenGLControl1MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: integer);
begin
uistate.mousedown := 0;
end;
initialization
uistate.Init;
end.