mirror of
				https://gitlab.com/freepascal.org/lazarus/lazarus.git
				synced 2025-10-31 13:41:35 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			551 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
			
		
		
	
	
			551 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
| unit imguimain;
 | |
| 
 | |
| {$mode objfpc}{$H+}
 | |
| 
 | |
| interface
 | |
| 
 | |
| uses
 | |
|   Classes, Forms, Controls, Graphics, ExtCtrls, OpenGLContext, LazLogger,
 | |
|   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.
 | |
| 
 | 
