2011-08-15 22:08:43 +00:00
|
|
|
//
|
|
|
|
// nvWidgets.h - User Interface library
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// Author: Ignacio Castano, Samuel Gateau, Evan Hart
|
|
|
|
// Email: sdkfeedback@nvidia.com
|
|
|
|
//
|
|
|
|
// Copyright (c) NVIDIA Corporation. All rights reserved.
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
unit nvWidgets;
|
|
|
|
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
|
|
|
|
interface
|
|
|
|
|
|
|
|
uses
|
2012-01-10 17:09:26 +00:00
|
|
|
Classes, SysUtils, nvBaseFont;
|
2011-08-15 22:08:43 +00:00
|
|
|
|
|
|
|
type
|
|
|
|
|
|
|
|
{ Point }
|
|
|
|
|
|
|
|
Point = object
|
|
|
|
x, y: integer;
|
|
|
|
|
|
|
|
constructor Point;
|
|
|
|
constructor Point(ix, iy: integer);
|
|
|
|
constructor Point(const p: Point);
|
|
|
|
end;
|
|
|
|
|
|
|
|
function SetPoint(x, y: integer): Point;
|
|
|
|
|
|
|
|
type
|
|
|
|
{ Rect }
|
|
|
|
|
|
|
|
Rect = object
|
|
|
|
x, y, w, h: integer;
|
|
|
|
|
|
|
|
constructor Rect;
|
|
|
|
constructor Rect(const p: TPoint);
|
|
|
|
constructor Rect(ix, iy: integer; iw: integer = 0; ih: integer = 0);
|
|
|
|
constructor Rect(const r: Rect);
|
|
|
|
end;
|
|
|
|
|
|
|
|
function SetRect(x, y: integer): Rect;
|
|
|
|
function SetRect(x, y, w, h: integer): Rect;
|
|
|
|
|
|
|
|
const
|
|
|
|
ButtonFlags_Off = $0;
|
|
|
|
ButtonFlags_On = $1;
|
|
|
|
ButtonFlags_Begin = $2;
|
|
|
|
ButtonFlags_End = $4;
|
|
|
|
ButtonFlags_Shift = $8;
|
|
|
|
ButtonFlags_Alt = $10;
|
|
|
|
ButtonFlags_Ctrl = $20;
|
|
|
|
|
|
|
|
type
|
|
|
|
ButtonState = record
|
|
|
|
state: integer;
|
|
|
|
time: TDateTime;
|
|
|
|
cursor: Point;
|
|
|
|
end;
|
|
|
|
|
|
|
|
const
|
|
|
|
// An enum to identify the mouse buttons
|
|
|
|
MouseButton_Left = 1;
|
|
|
|
MouseButton_Middle = 2;
|
|
|
|
MouseButton_Right = 3;
|
|
|
|
|
|
|
|
// An enum to identify the special key buttons not translated with ASCII codes
|
|
|
|
Key_F1 = 128;
|
|
|
|
Key_F2 = 129;
|
|
|
|
Key_F3 = 130;
|
|
|
|
Key_F4 = 131;
|
|
|
|
Key_F5 = 132;
|
|
|
|
Key_F6 = 133;
|
|
|
|
Key_F7 = 134;
|
|
|
|
Key_F8 = 135;
|
|
|
|
Key_F9 = 136;
|
|
|
|
Key_F10 = 137;
|
|
|
|
Key_F11 = 138;
|
|
|
|
Key_F12 = 139;
|
|
|
|
|
|
|
|
Key_Left = 140;
|
|
|
|
Key_Up = 141;
|
|
|
|
Key_Right = 142;
|
|
|
|
Key_Down = 143;
|
|
|
|
Key_PageUp = 144;
|
|
|
|
Key_PageDown = 145;
|
|
|
|
Key_Home = 146;
|
|
|
|
Key_End = 147;
|
|
|
|
Key_Insert = 148;
|
|
|
|
|
|
|
|
// The various flags to modify the behavior of the groups
|
|
|
|
|
|
|
|
// Layout behavior flags
|
|
|
|
GroupFlags_LayoutNone = $01;
|
|
|
|
GroupFlags_LayoutVertical = $02;
|
|
|
|
GroupFlags_LayoutHorizontal = $04;
|
|
|
|
GroupFlags_LayoutMask = $07;
|
|
|
|
GroupFlags_LayoutXMask = $ffff xor GroupFlags_LayoutMask;
|
|
|
|
|
|
|
|
// Alignment flags for the widgets inserted in the group
|
2012-01-01 20:52:34 +00:00
|
|
|
GroupFlags_AlignLeft = $10;
|
2011-08-15 22:08:43 +00:00
|
|
|
GroupFlags_AlignRight = $20;
|
|
|
|
GroupFlags_AlignTop = $40;
|
|
|
|
GroupFlags_AlignBottom = $80;
|
|
|
|
GroupFlags_AlignMask = $f0;
|
|
|
|
GroupFlags_AlignXMask = $ffff xor GroupFlags_AlignMask;
|
|
|
|
|
|
|
|
// Start flags defining the starting origin of the group
|
2012-01-01 20:52:34 +00:00
|
|
|
GroupFlags_StartLeft = $100;
|
2011-08-15 22:08:43 +00:00
|
|
|
GroupFlags_StartRight = $200;
|
|
|
|
GroupFlags_StartTop = $400;
|
|
|
|
GroupFlags_StartBottom = $800;
|
|
|
|
GroupFlags_StartMask = $f00;
|
|
|
|
GroupFlags_StartXMask = $ffff xor GroupFlags_StartMask;
|
|
|
|
|
|
|
|
// Optional flags
|
2012-01-01 20:52:34 +00:00
|
|
|
GroupFlags_LayoutForce = $8000;
|
2011-08-15 22:08:43 +00:00
|
|
|
GroupFlags_LayoutDefault = $4000;
|
|
|
|
GroupFlags_LayoutNoMargin = $2000;
|
|
|
|
GroupFlags_LayoutNoSpace = $1000;
|
|
|
|
GroupFlags_GrowRightFromBottom = GroupFlags_LayoutHorizontal or GroupFlags_StartLeft or GroupFlags_AlignLeft or GroupFlags_StartBottom or GroupFlags_AlignBottom;
|
|
|
|
|
|
|
|
// Predefined configurations
|
|
|
|
GroupFlags_GrowRightFromTop = GroupFlags_LayoutHorizontal or GroupFlags_StartLeft or GroupFlags_AlignLeft or GroupFlags_StartTop or GroupFlags_AlignTop;
|
|
|
|
GroupFlags_GrowLeftFromBottom = GroupFlags_LayoutHorizontal or GroupFlags_StartRight or GroupFlags_AlignRight or GroupFlags_StartBottom or GroupFlags_AlignBottom;
|
|
|
|
GroupFlags_GrowLeftFromTop = GroupFlags_LayoutHorizontal or GroupFlags_StartRight or GroupFlags_AlignRight or GroupFlags_StartTop or GroupFlags_AlignTop;
|
|
|
|
GroupFlags_GrowUpFromLeft = GroupFlags_LayoutVertical or GroupFlags_StartBottom or GroupFlags_AlignBottom or GroupFlags_StartLeft or GroupFlags_AlignLeft;
|
|
|
|
GroupFlags_GrowUpFromRight = GroupFlags_LayoutVertical or GroupFlags_StartBottom or GroupFlags_AlignBottom or GroupFlags_StartRight or GroupFlags_AlignRight;
|
|
|
|
GroupFlags_GrowDownFromLeft = GroupFlags_LayoutVertical or GroupFlags_StartTop or GroupFlags_AlignTop or GroupFlags_StartLeft or GroupFlags_AlignLeft;
|
|
|
|
GroupFlags_GrowDownFromRight = GroupFlags_LayoutVertical or GroupFlags_StartTop or GroupFlags_AlignTop or GroupFlags_StartRight or GroupFlags_AlignRight;
|
|
|
|
GroupFlags_LayoutDefaultFallback = GroupFlags_GrowDownFromLeft;
|
|
|
|
|
|
|
|
type
|
|
|
|
Group = record
|
|
|
|
bounds: Rect; //anchor point + width and height of the region
|
|
|
|
flags: integer; //group behavior
|
|
|
|
margin: integer; //border
|
|
|
|
space: integer; //interior
|
|
|
|
end;
|
|
|
|
PGroup = ^Group;
|
|
|
|
|
|
|
|
//*************************************************************************
|
|
|
|
// UIPainter
|
|
|
|
|
|
|
|
UIPainter = class(TObject)
|
2012-01-10 17:09:26 +00:00
|
|
|
private
|
|
|
|
FFont: TnvBaseFont;
|
|
|
|
procedure SetFont(AValue: TnvBaseFont);
|
2011-08-15 22:08:43 +00:00
|
|
|
public
|
|
|
|
constructor Create;
|
|
|
|
|
|
|
|
procedure _begin(const window: Rect); virtual;
|
|
|
|
procedure _end; virtual;
|
|
|
|
|
|
|
|
// These methods should be called between begin/end
|
|
|
|
|
|
|
|
procedure drawFrame(const r: Rect; margin: integer; style: integer); virtual; abstract;
|
|
|
|
|
|
|
|
function getLabelRect(const r: Rect; const Text: string; out rt: Rect; out nbLines: integer): Rect; virtual; abstract;
|
|
|
|
procedure drawLabel(const r: Rect; const Text: string; const rt: Rect; const nbLines: integer; isHover: boolean; style: integer); virtual; abstract;
|
|
|
|
|
|
|
|
function getButtonRect(const r: Rect; const Text: string; out rt: Rect): Rect; virtual; abstract;
|
|
|
|
procedure drawButton(const r: Rect; const Text: string; const rt: Rect; isDown: boolean; isHover: boolean; isFocus: boolean; style: integer); virtual; abstract;
|
|
|
|
|
|
|
|
function getCheckRect(const r: Rect; const Text: string; out rt: Rect; out rc: Rect): Rect; virtual; abstract;
|
|
|
|
procedure drawCheckButton(const r: Rect; const Text: string; const rt: Rect; const rr: Rect; isChecked: boolean; isHover: boolean; isFocus: boolean; style: integer); virtual; abstract;
|
|
|
|
|
|
|
|
function getRadioRect(const r: Rect; const Text: string; out rt: Rect; out rr: Rect): Rect; virtual; abstract;
|
|
|
|
procedure drawRadioButton(const r: Rect; const Text: string; const rt: Rect; const rr: Rect; isOn: boolean; isHover: boolean; isFocus: boolean; style: integer); virtual; abstract;
|
|
|
|
|
|
|
|
function getHorizontalSliderRect(const r: Rect; out rs: Rect; v: double; out rc: Rect): Rect; virtual; abstract;
|
|
|
|
procedure drawHorizontalSlider(const r: Rect; rs: Rect; v: double; rc: Rect; isHover: boolean; style: integer); virtual; abstract;
|
|
|
|
|
|
|
|
function getItemRect(const r: Rect; const Text: string; out rt: Rect): Rect; virtual; abstract;
|
|
|
|
procedure drawListItem(const r: Rect; const Text: string; const rt: Rect; isSelected: boolean; isHover: boolean; style: integer); virtual; abstract;
|
|
|
|
|
|
|
|
function getListRect(const r: Rect; numOptions: integer; const options: array of string; out ri: Rect; out rt: Rect): Rect; virtual; abstract;
|
|
|
|
procedure drawListBox(const r: Rect; numOptions: integer; const options: array of string; const ri: Rect; const rt: Rect; selected: integer; hovered: integer; style: integer); virtual; abstract;
|
|
|
|
|
|
|
|
function getComboRect(const r: Rect; numOptions: integer; const options: array of string; selected: integer; out rt: Rect; out ra: Rect): Rect; virtual; abstract;
|
|
|
|
function getComboOptionsRect(const rCombo: Rect; numOptions: integer; const options: array of string; out ri: Rect; out rit: Rect): Rect; virtual; abstract;
|
|
|
|
procedure drawComboBox(const rect: Rect; numOptions: integer; const options: array of string; const rt: Rect; const ra: Rect; selected: integer; isHover: boolean; isFocus: boolean; style: integer); virtual; abstract;
|
|
|
|
procedure drawComboOptions(const rect: Rect; numOptions: integer; const options: array of string; const ri: Rect; const rit: Rect; selected: integer; hovered: integer; isHover: boolean; isFocus: boolean; style: integer); virtual; abstract;
|
|
|
|
|
|
|
|
function getLineEditRect(const r: Rect; const Text: string; out rt: Rect): Rect; virtual; abstract;
|
|
|
|
procedure drawLineEdit(const r: Rect; const Text: string; const rt: Rect; caretPos: integer; isSelected: boolean; isHover: boolean; style: integer); virtual; abstract;
|
|
|
|
|
|
|
|
function getPanelRect(const r: Rect; const Text: string; out rt: Rect; out ra: Rect): Rect; virtual; abstract;
|
|
|
|
procedure drawPanel(const rect: Rect; const Text: string; const rt: Rect; const ra: Rect; isUnfold: boolean; isHover: boolean; isFocus: boolean; style: integer); virtual; abstract;
|
|
|
|
|
|
|
|
function getTextureViewRect(const rect: Rect; out rt: Rect): Rect; virtual; abstract;
|
|
|
|
procedure drawTextureView(const rect: Rect; const texID: integer; const rt: Rect; const rz: Rect; mipLevel: integer; texelScale: double; texelOffset: double; r: integer; g: integer; b: integer; a: integer; style: integer); virtual; abstract;
|
|
|
|
|
|
|
|
// Eval widget dimensions
|
|
|
|
function getCanvasMargin: integer; virtual;
|
|
|
|
function getCanvasSpace: integer; virtual;
|
|
|
|
function getFontHeight: integer; virtual;
|
|
|
|
function getTextLineWidth(const Text: string): integer; virtual;
|
|
|
|
function getTextSize(const Text: string; out nbLines: integer): integer; virtual;
|
|
|
|
function getPickedCharNb(const Text: string; const at: Point): integer; virtual;
|
|
|
|
|
|
|
|
procedure drawDebugRect(const r: Rect); virtual; abstract;
|
|
|
|
|
|
|
|
procedure init; virtual; abstract;
|
2012-01-10 17:09:26 +00:00
|
|
|
published
|
|
|
|
property Font: TnvBaseFont read FFont write SetFont;
|
2011-08-15 22:08:43 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
{ UIContext }
|
|
|
|
|
|
|
|
UIContext = class(TObject)
|
|
|
|
public
|
2012-01-10 17:09:26 +00:00
|
|
|
constructor Create;
|
|
|
|
destructor Destroy; override;
|
2011-08-15 22:08:43 +00:00
|
|
|
|
|
|
|
// UI method for processing window size events
|
|
|
|
//////////////////////////////////////////////////////////////////
|
2012-01-03 00:01:04 +00:00
|
|
|
procedure reshape(w, h: integer);
|
2011-08-15 22:08:43 +00:00
|
|
|
|
2012-01-01 21:30:27 +00:00
|
|
|
//
|
2011-08-15 22:08:43 +00:00
|
|
|
// Check if the UI is currently on Focus
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
function isOnFocus: boolean;
|
|
|
|
|
2012-01-01 21:30:27 +00:00
|
|
|
//
|
2011-08-15 22:08:43 +00:00
|
|
|
// UI method for processing mouse events
|
|
|
|
//////////////////////////////////////////////////////////////////
|
2012-01-03 00:01:04 +00:00
|
|
|
procedure mouse(button, state, modifier, x, y: integer);
|
|
|
|
procedure mouse(button, state, x, y: integer);
|
2011-08-15 22:08:43 +00:00
|
|
|
|
2012-01-01 21:30:27 +00:00
|
|
|
//
|
2011-08-15 22:08:43 +00:00
|
|
|
// UI method for processing mouse motion events
|
|
|
|
//////////////////////////////////////////////////////////////////
|
2012-01-03 00:01:04 +00:00
|
|
|
procedure mouseMotion(x, y: integer);
|
2011-08-15 22:08:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
// UI method for processing key events
|
|
|
|
//////////////////////////////////////////////////////////////////
|
2012-01-03 00:01:04 +00:00
|
|
|
procedure keyboard(k: byte; x, y: integer);
|
2011-08-15 22:08:43 +00:00
|
|
|
|
2012-01-01 21:30:27 +00:00
|
|
|
//
|
2011-08-15 22:08:43 +00:00
|
|
|
// UI method for entering UI processing mode
|
|
|
|
//
|
|
|
|
// This function must be used to begin the UI processing
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
procedure _begin;
|
|
|
|
|
|
|
|
// UI method for leaving UI processing mode
|
|
|
|
//
|
|
|
|
// This function must be used to end the UI processing
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
procedure _end; virtual;
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
// UI element processing
|
|
|
|
//
|
|
|
|
// The following methods provide the interface for rendering and querying
|
|
|
|
// UI objects. These methods must be called between begin/end.
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2012-01-01 21:30:27 +00:00
|
|
|
//
|
2011-08-15 22:08:43 +00:00
|
|
|
// UI method for drawing a static text label
|
|
|
|
// The label display a non interactive text.
|
|
|
|
// The text can have multiple lines
|
|
|
|
//
|
|
|
|
// rect - optionally provides a location and size for the label
|
|
|
|
// text - Text to display for the label (can have several lines)
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
procedure doLabel(const r: Rect; const Text: string; style: integer = 0);
|
|
|
|
|
2012-01-01 21:30:27 +00:00
|
|
|
//
|
2011-08-15 22:08:43 +00:00
|
|
|
// UI method for rendering and processing a push button
|
|
|
|
//
|
|
|
|
// rect - optionally provides a location and size for the button
|
|
|
|
// text - text to display on the button
|
|
|
|
// state - whether the button is depressed
|
|
|
|
// if state is NULL; the buttoin behave like a touch button
|
|
|
|
// else; the button behave like a toggle button
|
|
|
|
// style - optional style flag to modify the look
|
|
|
|
//
|
|
|
|
// @return True if the button'state changed
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
function doButton(const r: Rect; const Text: string; var state: boolean; style: integer = 0): boolean;
|
|
|
|
function doButton(const r: Rect; const Text: string): boolean;
|
|
|
|
|
|
|
|
// UI method for rendering and processing a check button
|
|
|
|
// Check button behaves similarly as a toggle button
|
|
|
|
// used to display and edit a bool property.
|
|
|
|
//
|
|
|
|
// rect - optionally provides a location and size for the button
|
|
|
|
// text - text to display on the button
|
|
|
|
// state - whether the check button is checked or not
|
2012-01-01 23:10:14 +00:00
|
|
|
// if state is NULL; the button behave like if a touch button unchecked
|
2011-08-15 22:08:43 +00:00
|
|
|
// style - optional style flag to modify the look
|
|
|
|
//
|
2012-01-01 23:10:14 +00:00
|
|
|
// @return True if the check button state changed
|
2011-08-15 22:08:43 +00:00
|
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
function doCheckButton(const r: Rect; const Text: string; var state: boolean; style: integer = 0): boolean;
|
|
|
|
|
2012-01-01 21:30:27 +00:00
|
|
|
//
|
2011-08-15 22:08:43 +00:00
|
|
|
// UI method for rendering and processing a radio button
|
|
|
|
// Radio buttons are typically used in groups to diplay and edit
|
|
|
|
// the possible reference values taken by an int value.
|
|
|
|
//
|
|
|
|
// One radio button is representing a possible reference value taken by the current value.
|
|
|
|
// It is displaying a boolean state true if the current value is equal to the reference value.
|
|
|
|
//
|
|
|
|
// reference - The reference int value represented by this radio button.
|
|
|
|
// rect - optionally provides a location and size for the button
|
|
|
|
// text - text to display on the button
|
|
|
|
// value - The value parameter compared to the reference value parameter.
|
|
|
|
// if value is NULL; the radio button is off
|
|
|
|
// style - optional style flag to modify the look
|
|
|
|
//
|
2012-01-01 23:10:14 +00:00
|
|
|
// @return True if the radio button value changed
|
2011-08-15 22:08:43 +00:00
|
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
function doRadioButton(reference: integer; const r: Rect; const Text: string; var Value: integer; style: integer = 0): boolean;
|
|
|
|
|
2012-01-01 21:30:27 +00:00
|
|
|
//
|
2011-08-15 22:08:43 +00:00
|
|
|
// UI method for rendering and processing a horizontal slider
|
|
|
|
// Horizontal slider is used to edit and display a scalar value in the specified range [min; max].
|
|
|
|
//
|
|
|
|
// rect - optionally provides a location and size for the widget
|
|
|
|
// min - min bound of the varying range of the value
|
|
|
|
// max - max bound of the varying range of the value
|
2012-01-01 23:10:14 +00:00
|
|
|
// value - the value edited by the widget
|
2011-08-15 22:08:43 +00:00
|
|
|
// if value is NULL; the value is set to min
|
|
|
|
// style - optional style flag to modify the look
|
|
|
|
//
|
2012-01-01 23:10:14 +00:00
|
|
|
// @return True if the slider value changed
|
2011-08-15 22:08:43 +00:00
|
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
function doHorizontalSlider(const aRect: Rect; min: double; max: double; var Value: double; style: integer = 0): boolean;
|
|
|
|
|
|
|
|
function doListItem(index: integer; const aRect: Rect; const Text: string; selected: integer; style: integer = 0): boolean;
|
|
|
|
function doListBox(const aRect: Rect; numOptions: integer; const options: array of string; var selected: integer; style: integer = 0): boolean;
|
|
|
|
function doComboBox(const aRect: Rect; numOptions: integer; const options: array of string; var selected: integer; style: integer = 0): boolean;
|
|
|
|
|
|
|
|
function doLineEdit(const aRect: Rect; var Text: string; maxTextLength: integer; out nbCharsReturned: integer; style: integer = 0): boolean;
|
|
|
|
|
|
|
|
procedure beginGroup(groupFlags: integer; const r: Rect); overload;
|
|
|
|
procedure beginGroup(groupFlags: integer = GroupFlags_LayoutDefault); overload;
|
|
|
|
procedure endGroup;
|
|
|
|
|
|
|
|
procedure beginFrame(groupFlags: integer = GroupFlags_LayoutDefault; const rect: Rect = 0; style: integer = 0);
|
|
|
|
procedure endFrame;
|
|
|
|
|
2012-01-07 21:16:10 +00:00
|
|
|
function beginPanel(var r: Rect; const Text: string; var isUnfold: boolean; groupFlags: integer = GroupFlags_LayoutDefault; style: integer = 0): boolean;
|
2011-08-15 22:08:43 +00:00
|
|
|
procedure endPanel;
|
|
|
|
|
|
|
|
function getGroupWidth: integer;
|
|
|
|
function getGroupHeight: integer;
|
|
|
|
|
|
|
|
function getCursorX: integer;
|
|
|
|
function getCursorY: integer;
|
|
|
|
|
|
|
|
function getMouseState(button: integer): ButtonState;
|
|
|
|
|
2012-01-01 21:30:27 +00:00
|
|
|
//
|
2011-08-15 22:08:43 +00:00
|
|
|
// UI method for drawing a texture view
|
|
|
|
// Several parameters control the equation used to display the texel
|
|
|
|
// texel = texture2DFetch(...);
|
|
|
|
// pixel = texelSwizzling( texel * texelScale + texelOffset );
|
|
|
|
//
|
|
|
|
// rect - provides a location and size for the texture view
|
|
|
|
// texID - texture identifier (Graphics API specific)
|
|
|
|
// zoomRect - rectangle area of the texture displayed
|
|
|
|
// mipLevel - mip Level of the texture displayed
|
|
|
|
// texelScale - scale applyed to the texel fetch
|
|
|
|
// texelOffset - offset applyed to the texel after scale
|
|
|
|
// texelSwizzling - swizzle applyed to the texel (after scale and offset)
|
|
|
|
// style - optional style flag to modify the look
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
procedure doTextureView(const aRect: Rect; const texID: integer; zoomRect: Rect; mipLevel: integer = -1; texelScale: double = 1; texelOffset: double = 0; red: integer = 0; green: integer = 1; blue: integer = 2; alpha: integer = 3; style: integer = 0);
|
|
|
|
|
|
|
|
protected
|
|
|
|
function getPainter: UIPainter;
|
|
|
|
|
|
|
|
function window: Rect;
|
|
|
|
|
|
|
|
private
|
2012-01-10 17:09:26 +00:00
|
|
|
FPainter: UIPainter;
|
2011-08-15 22:08:43 +00:00
|
|
|
procedure setCursor(x: integer; y: integer);
|
|
|
|
|
|
|
|
function overlap(const aRect: Rect; const p: Point): boolean;
|
|
|
|
|
|
|
|
function hasFocus(const aRect: Rect): boolean;
|
|
|
|
function isHover(const aRect: Rect): boolean;
|
2012-01-10 17:09:26 +00:00
|
|
|
procedure SetPainter(AValue: UIPainter);
|
2011-08-15 22:08:43 +00:00
|
|
|
protected
|
|
|
|
function placeRect(const r: Rect): Rect;
|
|
|
|
|
2012-01-10 17:09:26 +00:00
|
|
|
published
|
|
|
|
property Painter: UIPainter read FPainter write SetPainter;
|
2011-08-15 22:08:43 +00:00
|
|
|
|
2012-01-10 17:09:26 +00:00
|
|
|
protected
|
2011-08-15 22:08:43 +00:00
|
|
|
m_groupIndex: integer;
|
|
|
|
m_groupStack: array [0..63] of Group;
|
|
|
|
|
|
|
|
m_window: Rect;
|
|
|
|
|
|
|
|
m_currentCursor: Point;
|
|
|
|
m_mouseButton: array [0..2] of ButtonState;
|
|
|
|
m_keyBuffer: array [0..31] of byte;
|
|
|
|
m_nbKeys: integer;
|
|
|
|
|
|
|
|
m_focusCaretPos: integer;
|
|
|
|
m_focusPoint: Point;
|
|
|
|
m_twoStepFocus: boolean;
|
|
|
|
m_uiOnFocus: boolean;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function EvalBool(b: boolean): byte;
|
|
|
|
|
|
|
|
implementation
|
|
|
|
|
|
|
|
uses
|
|
|
|
Math;
|
|
|
|
|
|
|
|
function EvalBool(b: boolean): byte;
|
|
|
|
begin
|
|
|
|
if b then
|
|
|
|
Result := 1
|
|
|
|
else
|
|
|
|
Result := 0;
|
|
|
|
end;
|
|
|
|
|
|
|
|
{ Point }
|
|
|
|
|
|
|
|
constructor Point.Point;
|
|
|
|
begin
|
|
|
|
x := 0;
|
|
|
|
y := 0;
|
|
|
|
end;
|
|
|
|
|
|
|
|
constructor Point.Point(ix, iy: integer);
|
|
|
|
begin
|
|
|
|
x := ix;
|
|
|
|
y := iy;
|
|
|
|
end;
|
|
|
|
|
|
|
|
constructor Point.Point(const p: Point);
|
|
|
|
begin
|
|
|
|
x := p.x;
|
|
|
|
y := p.y;
|
|
|
|
end;
|
|
|
|
|
|
|
|
{ Rect }
|
|
|
|
|
|
|
|
constructor Rect.Rect;
|
|
|
|
begin
|
|
|
|
x := 0;
|
|
|
|
y := 0;
|
|
|
|
w := 0;
|
|
|
|
h := 0;
|
|
|
|
end;
|
|
|
|
|
|
|
|
constructor Rect.Rect(const p: TPoint);
|
|
|
|
begin
|
|
|
|
x := p.x;
|
|
|
|
y := p.y;
|
|
|
|
w := 0;
|
|
|
|
h := 0;
|
|
|
|
end;
|
|
|
|
|
|
|
|
constructor Rect.Rect(ix, iy: integer; iw: integer; ih: integer);
|
|
|
|
begin
|
|
|
|
x := ix;
|
|
|
|
y := iy;
|
|
|
|
w := iw;
|
|
|
|
h := ih;
|
|
|
|
end;
|
|
|
|
|
|
|
|
constructor Rect.Rect(const r: Rect);
|
|
|
|
begin
|
|
|
|
x := r.x;
|
|
|
|
y := r.y;
|
|
|
|
w := r.w;
|
|
|
|
h := r.h;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function SetPoint(x, y: integer): Point;
|
|
|
|
begin
|
|
|
|
Result.Point(x, y);
|
|
|
|
end;
|
|
|
|
|
|
|
|
function SetRect(x, y: integer): Rect;
|
|
|
|
begin
|
|
|
|
Result.Rect(x, y);
|
|
|
|
end;
|
|
|
|
|
|
|
|
function SetRect(x, y, w, h: integer): Rect;
|
|
|
|
begin
|
|
|
|
Result.Rect(x, y, w, h);
|
|
|
|
end;
|
|
|
|
|
|
|
|
{ UIContext }
|
|
|
|
|
2012-01-10 17:09:26 +00:00
|
|
|
constructor UIContext.Create;
|
2011-08-15 22:08:43 +00:00
|
|
|
begin
|
|
|
|
m_twoStepFocus := False;
|
|
|
|
m_focusCaretPos := -1;
|
|
|
|
end;
|
|
|
|
|
2012-01-10 17:09:26 +00:00
|
|
|
destructor UIContext.Destroy;
|
|
|
|
begin
|
|
|
|
if Assigned(Painter) then
|
|
|
|
Painter.Free;
|
|
|
|
|
|
|
|
inherited Destroy;
|
|
|
|
end;
|
|
|
|
|
2012-01-03 00:01:04 +00:00
|
|
|
procedure UIContext.reshape(w, h: integer);
|
2011-08-15 22:08:43 +00:00
|
|
|
begin
|
|
|
|
m_window.x := 0;
|
|
|
|
m_window.y := 0;
|
|
|
|
m_window.w := w;
|
|
|
|
m_window.h := h;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function UIContext.isOnFocus: boolean;
|
|
|
|
begin
|
|
|
|
Result := m_uiOnFocus;
|
|
|
|
end;
|
|
|
|
|
2012-01-03 00:01:04 +00:00
|
|
|
procedure UIContext.mouse(button, state, modifier, x, y: integer);
|
2011-08-15 22:08:43 +00:00
|
|
|
var
|
|
|
|
idx: integer;
|
|
|
|
begin
|
|
|
|
setCursor(x, y);
|
|
|
|
|
|
|
|
idx := -1;
|
2012-01-03 00:01:04 +00:00
|
|
|
case button of
|
|
|
|
MouseButton_Left: idx := 0;
|
|
|
|
MouseButton_Middle: idx := 1;
|
|
|
|
MouseButton_Right: idx := 2;
|
|
|
|
end;
|
2011-08-15 22:08:43 +00:00
|
|
|
|
|
|
|
modifier := modifier and (ButtonFlags_Alt or ButtonFlags_Shift or ButtonFlags_Ctrl);
|
|
|
|
|
|
|
|
if idx >= 0 then
|
|
|
|
begin
|
|
|
|
if state = 1 then
|
|
|
|
begin
|
|
|
|
m_mouseButton[idx].state := ButtonFlags_On or ButtonFlags_Begin or modifier;
|
|
|
|
m_mouseButton[idx].time := Now;
|
|
|
|
m_mouseButton[idx].cursor.x := x;
|
|
|
|
m_mouseButton[idx].cursor.y := m_window.h - y;
|
|
|
|
end;
|
|
|
|
if state = 0 then
|
|
|
|
m_mouseButton[idx].state := ButtonFlags_On or ButtonFlags_End or modifier;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2012-01-03 00:01:04 +00:00
|
|
|
procedure UIContext.mouse(button, state, x, y: integer);
|
2011-08-15 22:08:43 +00:00
|
|
|
begin
|
|
|
|
mouse(button, state, 0, x, y);
|
|
|
|
end;
|
|
|
|
|
2012-01-03 00:01:04 +00:00
|
|
|
procedure UIContext.mouseMotion(x, y: integer);
|
2011-08-15 22:08:43 +00:00
|
|
|
begin
|
|
|
|
setCursor(x, y);
|
|
|
|
end;
|
|
|
|
|
2012-01-03 00:01:04 +00:00
|
|
|
procedure UIContext.keyboard(k: byte; x, y: integer);
|
2011-08-15 22:08:43 +00:00
|
|
|
begin
|
|
|
|
setCursor(x, y);
|
|
|
|
m_keyBuffer[m_nbKeys] := k;
|
|
|
|
Inc(m_nbKeys);
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure UIContext._begin;
|
|
|
|
begin
|
2012-01-10 17:09:26 +00:00
|
|
|
Painter._begin(m_window);
|
2011-08-15 22:08:43 +00:00
|
|
|
|
|
|
|
m_groupIndex := 0;
|
|
|
|
m_groupStack[m_groupIndex].flags := GroupFlags_LayoutNone;
|
2012-01-10 17:09:26 +00:00
|
|
|
m_groupStack[m_groupIndex].margin := Painter.getCanvasMargin;
|
|
|
|
m_groupStack[m_groupIndex].space := Painter.getCanvasSpace;
|
2011-08-15 22:08:43 +00:00
|
|
|
m_groupStack[m_groupIndex].bounds := m_window;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure UIContext._end;
|
|
|
|
var
|
|
|
|
i: integer;
|
|
|
|
begin
|
2012-01-10 17:09:26 +00:00
|
|
|
Painter._end;
|
2011-08-15 22:08:43 +00:00
|
|
|
|
|
|
|
// Release focus.
|
|
|
|
if (m_mouseButton[0].state and ButtonFlags_End) > 0 then
|
|
|
|
m_uiOnFocus := False;
|
|
|
|
|
|
|
|
// Update state for next frame.
|
|
|
|
for i := 0 to 2 do
|
|
|
|
begin
|
|
|
|
if (m_mouseButton[i].state and ButtonFlags_Begin) > 0 then
|
|
|
|
m_mouseButton[i].state := m_mouseButton[i].state xor ButtonFlags_Begin;
|
|
|
|
//else
|
|
|
|
if (m_mouseButton[i].state and ButtonFlags_End) > 0 then
|
|
|
|
m_mouseButton[i].state := ButtonFlags_Off;
|
|
|
|
end;
|
|
|
|
|
|
|
|
// Flush key buffer
|
|
|
|
m_nbKeys := 0;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure UIContext.doLabel(const r: Rect; const Text: string; style: integer);
|
|
|
|
var
|
|
|
|
rt: Rect;
|
|
|
|
nbLines: integer;
|
|
|
|
aRect: Rect;
|
|
|
|
begin
|
2012-01-10 17:09:26 +00:00
|
|
|
aRect := placeRect(Painter.getLabelRect(r, Text, rt, nbLines));
|
|
|
|
Painter.drawLabel(aRect, Text, rt, nbLines, isHover(aRect), style);
|
2011-08-15 22:08:43 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
function UIContext.doButton(const r: Rect; const Text: string; var state: boolean; style: integer): boolean;
|
|
|
|
var
|
|
|
|
rt: Rect;
|
|
|
|
aRect: Rect;
|
|
|
|
focus: boolean;
|
|
|
|
hover: boolean;
|
|
|
|
isDown: boolean;
|
|
|
|
begin
|
2012-01-10 17:09:26 +00:00
|
|
|
aRect := placeRect(Painter.getButtonRect(r, Text, rt));
|
2011-08-15 22:08:43 +00:00
|
|
|
focus := hasFocus(aRect);
|
|
|
|
hover := isHover(aRect);
|
|
|
|
|
|
|
|
isDown := state;
|
|
|
|
//isDown := ((m_mouseButton[0].state and ButtonFlags_On)>0) and hover and focus;
|
|
|
|
|
2012-01-10 17:09:26 +00:00
|
|
|
Painter.drawButton(aRect, Text, rt, isDown, hover, focus, style);
|
2011-08-15 22:08:43 +00:00
|
|
|
|
|
|
|
if not focus then
|
|
|
|
m_uiOnFocus := True;
|
|
|
|
|
|
|
|
if ((m_mouseButton[0].state and ButtonFlags_End) > 0) and focus and overlap(aRect, m_currentCursor) then
|
|
|
|
begin
|
|
|
|
state := not state;
|
|
|
|
Result := True;
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
|
|
|
|
Result := False;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function UIContext.doButton(const r: Rect; const Text: string): boolean;
|
|
|
|
var
|
|
|
|
tmp: boolean = False;
|
|
|
|
begin
|
|
|
|
Result := doButton(r, Text, tmp);
|
|
|
|
end;
|
|
|
|
|
|
|
|
function UIContext.doCheckButton(const r: Rect; const Text: string; var state: boolean; style: integer): boolean;
|
|
|
|
var
|
|
|
|
rt: Rect;
|
|
|
|
rc: Rect;
|
|
|
|
aRect: Rect;
|
|
|
|
focus: boolean;
|
|
|
|
hover: boolean;
|
|
|
|
begin
|
2012-01-10 17:09:26 +00:00
|
|
|
aRect := placeRect(Painter.getCheckRect(r, Text, rt, rc));
|
2011-08-15 22:08:43 +00:00
|
|
|
focus := hasFocus(aRect);
|
|
|
|
hover := isHover(aRect);
|
2012-01-10 17:09:26 +00:00
|
|
|
Painter.drawCheckButton(aRect, Text, rt, rc, state, hover, focus, style);
|
2011-08-15 22:08:43 +00:00
|
|
|
|
|
|
|
if hasFocus(aRect) then
|
|
|
|
m_uiOnFocus := True;
|
|
|
|
|
|
|
|
if ((m_mouseButton[0].state and ButtonFlags_End) > 0) and focus and overlap(aRect, m_currentCursor) then
|
|
|
|
begin
|
|
|
|
state := not state;
|
|
|
|
Result := True;
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
|
|
|
|
Result := False;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function UIContext.doRadioButton(reference: integer; const r: Rect; const Text: string; var Value: integer; style: integer): boolean;
|
|
|
|
var
|
|
|
|
rr: Rect;
|
|
|
|
rt: Rect;
|
|
|
|
aRect: Rect;
|
|
|
|
focus: boolean;
|
|
|
|
hover: boolean;
|
|
|
|
begin
|
2012-01-10 17:09:26 +00:00
|
|
|
aRect := placeRect(Painter.getRadioRect(r, Text, rt, rr));
|
2011-08-15 22:08:43 +00:00
|
|
|
focus := hasFocus(aRect);
|
|
|
|
hover := isHover(aRect);
|
2012-01-10 17:09:26 +00:00
|
|
|
Painter.drawRadioButton(aRect, Text, rt, rr, longbool(Value and EvalBool(reference = Value)), hover, focus, style);
|
2011-08-15 22:08:43 +00:00
|
|
|
|
|
|
|
if focus then
|
|
|
|
m_uiOnFocus := True;
|
|
|
|
|
|
|
|
if ((m_mouseButton[0].state and ButtonFlags_End) > 0) and focus and overlap(aRect, m_currentCursor) then
|
|
|
|
begin
|
|
|
|
Value := reference;
|
|
|
|
Result := True;
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
|
|
|
|
Result := False;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function UIContext.doHorizontalSlider(const aRect: Rect; min: double; max: double; var Value: double; style: integer): boolean;
|
|
|
|
var
|
|
|
|
f: double;
|
|
|
|
rs: Rect;
|
|
|
|
rc: Rect;
|
|
|
|
rr: Rect;
|
|
|
|
changed: boolean = False;
|
|
|
|
xs: integer;
|
|
|
|
x: integer;
|
|
|
|
begin
|
|
|
|
// Map current value to 0-1.
|
|
|
|
f := (Value - min) / (max - min);
|
|
|
|
|
|
|
|
if f < 0 then
|
|
|
|
f := 0
|
|
|
|
else
|
|
|
|
if f > 1 then
|
|
|
|
f := 1;
|
|
|
|
|
2012-01-10 17:09:26 +00:00
|
|
|
rr := placeRect(Painter.getHorizontalSliderRect(aRect, rs, f, rc));
|
2011-08-15 22:08:43 +00:00
|
|
|
|
|
|
|
if hasFocus(rr) then
|
|
|
|
begin
|
|
|
|
m_uiOnFocus := True;
|
|
|
|
xs := rr.x + rs.x + rc.w div 2;
|
|
|
|
x := m_currentCursor.x - xs;
|
|
|
|
|
|
|
|
if x < 0 then
|
|
|
|
x := 0
|
|
|
|
else
|
|
|
|
if x > rs.w then
|
|
|
|
x := rs.w;
|
|
|
|
|
|
|
|
rc.x := x;
|
|
|
|
|
|
|
|
f := x / rs.w;
|
|
|
|
f := f * (max - min) + min;
|
|
|
|
|
|
|
|
if abs(f - Value) > (max - min) * 0.01 then
|
|
|
|
begin
|
|
|
|
changed := True;
|
|
|
|
Value := f;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2012-01-10 17:09:26 +00:00
|
|
|
Painter.drawHorizontalSlider(rr, rs, f, rc, isHover(rr), style);
|
2011-08-15 22:08:43 +00:00
|
|
|
|
|
|
|
Result := changed;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function UIContext.doListItem(index: integer; const aRect: Rect; const Text: string; selected: integer; style: integer): boolean;
|
|
|
|
var
|
|
|
|
rt: Rect;
|
|
|
|
r: Rect;
|
|
|
|
begin
|
2012-01-10 17:09:26 +00:00
|
|
|
r := placeRect(Painter.getItemRect(aRect, Text, rt));
|
|
|
|
Painter.drawListItem(r, Text, rt, longbool(selected and EvalBool(index = selected)), isHover(r), style);
|
2011-08-15 22:08:43 +00:00
|
|
|
|
|
|
|
Result := isHover(r);
|
|
|
|
end;
|
|
|
|
|
|
|
|
function UIContext.doListBox(const aRect: Rect; numOptions: integer; const options: array of string; var selected: integer; style: integer): boolean;
|
|
|
|
var
|
|
|
|
ri: Rect;
|
|
|
|
rt: Rect;
|
|
|
|
rr: Rect;
|
|
|
|
focus: boolean;
|
|
|
|
hover: boolean;
|
|
|
|
hovered: integer = -1;
|
|
|
|
lSelected: integer = -1;
|
|
|
|
begin
|
2012-01-10 17:09:26 +00:00
|
|
|
rr := placeRect(Painter.getListRect(aRect, numOptions, options, ri, rt));
|
2011-08-15 22:08:43 +00:00
|
|
|
focus := hasFocus(rr);
|
|
|
|
hover := isHover(rr);
|
|
|
|
|
|
|
|
if hover then
|
|
|
|
hovered := numOptions - 1 - (m_currentCursor.y - (rr.y + ri.y)) div (ri.h);
|
|
|
|
|
|
|
|
if selected <> 0 then
|
|
|
|
lSelected := selected;
|
|
|
|
|
2012-01-10 17:09:26 +00:00
|
|
|
Painter.drawListBox(rr, numOptions, options, ri, rt, lSelected, hovered, style);
|
2011-08-15 22:08:43 +00:00
|
|
|
|
|
|
|
if focus then
|
|
|
|
m_uiOnFocus := True;
|
|
|
|
|
|
|
|
if ((m_mouseButton[0].state and ButtonFlags_End) > 0) and focus and overlap(rr, m_currentCursor) and (lSelected <> hovered) then
|
|
|
|
begin
|
|
|
|
selected := hovered;
|
|
|
|
Result := True;
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
|
|
|
|
Result := False;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function UIContext.doComboBox(const aRect: Rect; numOptions: integer; const options: array of string; var selected: integer; style: integer): boolean;
|
|
|
|
var
|
|
|
|
rt: Rect;
|
|
|
|
ra: Rect;
|
|
|
|
rr: Rect;
|
|
|
|
focus: boolean;
|
|
|
|
hover: boolean;
|
|
|
|
ro: Rect;
|
|
|
|
ri: Rect;
|
|
|
|
rit: Rect;
|
|
|
|
hovered: integer;
|
|
|
|
hoverOptions: boolean;
|
|
|
|
begin
|
|
|
|
// First get the rect of the combobox itself and do some test with it
|
2012-01-10 17:09:26 +00:00
|
|
|
rr := placeRect(Painter.getComboRect(aRect, numOptions, options, selected, rt, ra));
|
2011-08-15 22:08:43 +00:00
|
|
|
focus := hasFocus(rr);
|
|
|
|
hover := isHover(rr);
|
|
|
|
|
|
|
|
if focus then
|
|
|
|
begin
|
|
|
|
m_uiOnFocus := True;
|
|
|
|
|
|
|
|
// then if the combo box has focus, we can look for the geometry of the options frame
|
2012-01-10 17:09:26 +00:00
|
|
|
ro := Painter.getComboOptionsRect(rr, numOptions, options, ri, rit);
|
2011-08-15 22:08:43 +00:00
|
|
|
hovered := -1;
|
|
|
|
hoverOptions := overlap(ro, m_currentCursor);
|
|
|
|
|
|
|
|
if hoverOptions then
|
|
|
|
hovered := numOptions - 1 - (m_currentCursor.y - (ro.y + ri.y)) div (ri.h);
|
|
|
|
|
|
|
|
// draw combo anyway
|
2012-01-10 17:09:26 +00:00
|
|
|
Painter.drawComboBox(rr, numOptions, options, rt, ra, selected, hover, focus, style);
|
2011-08-15 22:08:43 +00:00
|
|
|
|
|
|
|
// draw options
|
2012-01-10 17:09:26 +00:00
|
|
|
Painter.drawComboOptions(ro, numOptions, options, ri, rit, selected, hovered, hover, focus, style);
|
2011-08-15 22:08:43 +00:00
|
|
|
|
|
|
|
// When the widget get the focus, cache the focus point
|
|
|
|
if not m_twoStepFocus then
|
|
|
|
begin
|
|
|
|
if hover and ((m_mouseButton[0].state and ButtonFlags_End) > 0) then
|
|
|
|
begin
|
|
|
|
m_focusPoint := m_mouseButton[0].cursor;
|
|
|
|
m_twoStepFocus := True;
|
|
|
|
end;
|
|
|
|
end
|
|
|
|
else
|
|
|
|
begin
|
|
|
|
// Else Release the 2level focus when left mouse down out or up anyway
|
|
|
|
// replace the stored left mouse down pos with the focus point to keep focus
|
|
|
|
// on this widget for the next widget rendered in the frame
|
|
|
|
if not (hoverOptions or hover) and
|
|
|
|
(((m_mouseButton[0].state and ButtonFlags_Begin) > 0) or
|
|
|
|
((m_mouseButton[0].state and ButtonFlags_End > 0))) then
|
|
|
|
m_twoStepFocus := False
|
|
|
|
else
|
|
|
|
if (hoverOptions or hover) and ((m_mouseButton[0].state and ButtonFlags_End) > 0) then
|
|
|
|
begin
|
|
|
|
m_mouseButton[0].cursor := m_focusPoint;
|
|
|
|
m_twoStepFocus := False;
|
|
|
|
end;
|
|
|
|
|
|
|
|
if hoverOptions and ((m_mouseButton[0].state and ButtonFlags_Begin) > 0) then
|
|
|
|
m_mouseButton[0].cursor := m_focusPoint;
|
|
|
|
end;
|
|
|
|
|
|
|
|
// On mouse left bouton up, then select it
|
|
|
|
if (hovered > -1) and (hovered < numOptions) and ((m_mouseButton[0].state and ButtonFlags_End) > 0) then
|
|
|
|
begin
|
|
|
|
selected := hovered;
|
|
|
|
Result := True;
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
end
|
|
|
|
else
|
2012-01-10 17:09:26 +00:00
|
|
|
Painter.drawComboBox(rr, numOptions, options, rt, ra, selected, hover, focus, style);
|
2011-08-15 22:08:43 +00:00
|
|
|
|
|
|
|
Result := False;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function UIContext.doLineEdit(const aRect: Rect; var Text: string; maxTextLength: integer; out nbCharsReturned: integer; style: integer): boolean;
|
|
|
|
var
|
|
|
|
rt: Rect;
|
|
|
|
rr: Rect;
|
|
|
|
focus: boolean;
|
|
|
|
hover: boolean;
|
|
|
|
_result: boolean = False;
|
|
|
|
carretPos: integer = -1;
|
|
|
|
textLength: integer;
|
|
|
|
nbKeys: integer;
|
|
|
|
keyNb: integer;
|
|
|
|
begin
|
2012-01-10 17:09:26 +00:00
|
|
|
rr := placeRect(Painter.getLineEditRect(aRect, Text, rt));
|
2011-08-15 22:08:43 +00:00
|
|
|
focus := hasFocus(rr);
|
|
|
|
hover := isHover(rr);
|
|
|
|
|
|
|
|
if focus then
|
|
|
|
begin
|
|
|
|
m_uiOnFocus := True;
|
|
|
|
|
|
|
|
// When the widget get the focus, cache the focus point
|
|
|
|
if not m_twoStepFocus then
|
|
|
|
begin
|
|
|
|
m_twoStepFocus := True;
|
|
|
|
m_focusPoint := SetPoint(rr.x, rr.y);
|
|
|
|
end
|
|
|
|
else
|
|
|
|
begin
|
|
|
|
// Else Release the 2level focus when left mouse down out or up anyway
|
|
|
|
// replace the stored left mouse down pos with the focus point to keep focus
|
|
|
|
// on this widget for the next widget rendered in the frame
|
|
|
|
if not hover and
|
|
|
|
(((m_mouseButton[0].state and ButtonFlags_Begin) > 0) or
|
|
|
|
((m_mouseButton[0].state and ButtonFlags_End) > 0)) then
|
|
|
|
begin
|
|
|
|
m_twoStepFocus := False;
|
|
|
|
m_focusCaretPos := -1;
|
|
|
|
end;
|
|
|
|
|
|
|
|
if hover and ((m_mouseButton[0].state and ButtonFlags_Begin) > 0) then
|
|
|
|
m_mouseButton[0].cursor := m_focusPoint;
|
|
|
|
end;
|
|
|
|
|
|
|
|
// Eval caret pos on every click hover
|
|
|
|
if hover and ((m_mouseButton[0].state and ButtonFlags_Begin) > 0) then
|
2012-01-10 17:09:26 +00:00
|
|
|
m_focusCaretPos := Painter.getPickedCharNb(Text, SetPoint(m_currentCursor.x - rt.x - rr.x, m_currentCursor.y - rt.y - rr.y));
|
2011-08-15 22:08:43 +00:00
|
|
|
|
|
|
|
// If keys are buffered, apply input to the edited text
|
|
|
|
if m_nbKeys <> 0 then
|
|
|
|
begin
|
|
|
|
textLength := Length(Text);
|
|
|
|
|
|
|
|
if m_focusCaretPos = -1 then
|
|
|
|
m_focusCaretPos := textLength;
|
|
|
|
|
|
|
|
nbKeys := m_nbKeys;
|
|
|
|
keyNb := 0;
|
|
|
|
|
|
|
|
while nbKeys <> 0 do
|
|
|
|
begin
|
|
|
|
// filter for special keys
|
|
|
|
// Enter, quit edition
|
|
|
|
if m_keyBuffer[keyNb] = 13 then
|
|
|
|
begin
|
|
|
|
nbKeys := 1;
|
|
|
|
m_twoStepFocus := False;
|
|
|
|
m_focusCaretPos := -1;
|
|
|
|
end
|
|
|
|
else
|
|
|
|
// Special keys
|
|
|
|
if m_keyBuffer[keyNb] >= Key_F1 then
|
|
|
|
begin
|
|
|
|
case m_keyBuffer[keyNb] of
|
|
|
|
Key_Left:
|
|
|
|
// move cursor left one char
|
|
|
|
if m_focusCaretPos > 0 then
|
|
|
|
Dec(m_focusCaretPos);
|
|
|
|
Key_Right:
|
|
|
|
// move cursor right one char
|
|
|
|
if m_focusCaretPos <= textLength then
|
|
|
|
Inc(m_focusCaretPos);
|
|
|
|
Key_Home:
|
|
|
|
m_focusCaretPos := 0;
|
|
|
|
Key_End:
|
|
|
|
m_focusCaretPos := textLength + 1;
|
|
|
|
Key_Insert:
|
|
|
|
begin
|
|
|
|
end
|
|
|
|
else
|
|
|
|
// strange key pressed...
|
|
|
|
Dec(m_focusCaretPos);
|
|
|
|
end;
|
|
|
|
end
|
|
|
|
else
|
|
|
|
// Delete, move the chars >= carret back 1, carret stay in place
|
|
|
|
if m_keyBuffer[keyNb] = 127 then
|
|
|
|
Delete(Text,m_focusCaretPos, 1)
|
|
|
|
else
|
|
|
|
// Backspace, move the chars > carret back 1, carret move back 1
|
|
|
|
if m_keyBuffer[keyNb] = 8 then
|
|
|
|
begin
|
|
|
|
if m_focusCaretPos > 0 then
|
|
|
|
begin
|
|
|
|
Delete(Text,m_focusCaretPos-1, 1);
|
|
|
|
|
|
|
|
Dec(m_focusCaretPos);
|
|
|
|
Dec(textLength);
|
|
|
|
end;
|
|
|
|
end
|
|
|
|
else
|
|
|
|
// Regular char, append it to the edited string
|
|
|
|
if textLength < maxTextLength then
|
|
|
|
begin
|
|
|
|
Insert(Chr(m_keyBuffer[keyNb]), Text, m_focusCaretPos);
|
|
|
|
|
|
|
|
Inc(m_focusCaretPos);
|
|
|
|
Inc(textLength);
|
|
|
|
end;
|
|
|
|
|
|
|
|
Inc(keyNb);
|
|
|
|
Dec(nbKeys);
|
|
|
|
end;
|
|
|
|
nbCharsReturned := textLength;
|
|
|
|
_result := True;
|
|
|
|
end;
|
|
|
|
carretPos := m_focusCaretPos;
|
|
|
|
end;
|
|
|
|
|
2012-01-10 17:09:26 +00:00
|
|
|
Painter.drawLineEdit(rr, Text, rt, carretPos, focus, hover, style);
|
2011-08-15 22:08:43 +00:00
|
|
|
|
|
|
|
Result := _result;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure UIContext.beginGroup(groupFlags: integer; const r: Rect);
|
|
|
|
var
|
|
|
|
parentGroup: Group;
|
|
|
|
newGroup: PGroup;
|
|
|
|
parentLayout: integer;
|
|
|
|
parentAlign: integer;
|
|
|
|
newAlign: integer;
|
|
|
|
newStart: integer;
|
2012-01-02 11:24:43 +00:00
|
|
|
//newLayout: integer;
|
2011-08-15 22:08:43 +00:00
|
|
|
aRect: Rect;
|
|
|
|
begin
|
|
|
|
// Push one more group.
|
|
|
|
parentGroup := m_groupStack[m_groupIndex];
|
|
|
|
Inc(m_groupIndex);
|
|
|
|
newGroup := @m_groupStack[m_groupIndex];
|
|
|
|
|
|
|
|
// Assign layout behavior
|
|
|
|
parentLayout := parentGroup.flags and GroupFlags_LayoutMask;
|
|
|
|
parentAlign := parentGroup.flags and GroupFlags_AlignMask;
|
|
|
|
|
|
|
|
// If the groupFlags ask to force the layout then keep the newcanvas layout as is
|
|
|
|
// otherwise, adapt it to the parent's behavior
|
|
|
|
if ((groupFlags and GroupFlags_LayoutForce) = 0) or ((groupFlags and GroupFlags_LayoutNone) = 0) then
|
|
|
|
begin
|
|
|
|
// If default then use parent style except if none layout => default fallback
|
|
|
|
if (groupFlags and GroupFlags_LayoutDefault) > 0 then
|
|
|
|
begin
|
|
|
|
if (parentLayout and GroupFlags_LayoutNone) > 0 then
|
|
|
|
groupFlags := GroupFlags_LayoutDefaultFallback
|
|
|
|
else
|
|
|
|
groupFlags := parentGroup.flags;
|
|
|
|
end
|
|
|
|
else
|
|
|
|
if ((parentLayout and (GroupFlags_LayoutVertical or GroupFlags_LayoutHorizontal)) > 0) and
|
|
|
|
((groupFlags and (GroupFlags_LayoutVertical or GroupFlags_LayoutHorizontal)) > 0) then
|
|
|
|
groupFlags := (groupFlags and GroupFlags_AlignXMask) or parentAlign;
|
|
|
|
end;
|
|
|
|
|
2012-01-10 17:09:26 +00:00
|
|
|
newGroup^.margin := EvalBool((groupFlags and GroupFlags_LayoutNoMargin) = 0) * Painter.getCanvasMargin;
|
|
|
|
newGroup^.space := EvalBool((groupFlags and GroupFlags_LayoutNoSpace) = 0) * Painter.getCanvasSpace;
|
2011-08-15 22:08:43 +00:00
|
|
|
newGroup^.flags := groupFlags;
|
|
|
|
|
|
|
|
//newLayout := groupFlags and GroupFlags_LayoutMask;
|
|
|
|
newAlign := groupFlags and GroupFlags_AlignMask;
|
|
|
|
newStart := groupFlags and GroupFlags_StartMask;
|
|
|
|
|
|
|
|
// Place a regular rect in current group, this will be the new group rect start pos
|
|
|
|
aRect := r;
|
|
|
|
|
|
|
|
// Don't modify parent group bounds yet, done in endGroup
|
|
|
|
// Right now place the new group rect
|
|
|
|
if parentLayout = GroupFlags_LayoutNone then
|
|
|
|
begin
|
|
|
|
// Horizontal behavior.
|
|
|
|
aRect.x := aRect.x + (parentGroup.bounds.x + newGroup^.margin + EvalBool((newStart and GroupFlags_StartRight) > 0) * parentGroup.bounds.w - EvalBool((newAlign and GroupFlags_AlignRight) > 0) * (2 * newGroup^.margin + aRect.w));
|
|
|
|
// Vertical behavior.
|
|
|
|
aRect.y := aRect.y + (parentGroup.bounds.y + newGroup^.margin + EvalBool((newStart and GroupFlags_StartTop) > 0) * parentGroup.bounds.h - EvalBool((newAlign and GroupFlags_AlignTop) > 0) * (2 * newGroup^.margin + aRect.h));
|
|
|
|
end
|
|
|
|
else
|
2012-01-07 21:16:10 +00:00
|
|
|
if parentLayout = GroupFlags_LayoutVertical then
|
|
|
|
begin
|
|
|
|
// Horizontal behavior.
|
|
|
|
aRect.x := aRect.x + (parentGroup.bounds.x + newGroup^.margin + EvalBool((parentAlign and GroupFlags_AlignRight) > 0) * (parentGroup.bounds.w - 2 * newGroup^.margin - aRect.w));
|
2011-08-15 22:08:43 +00:00
|
|
|
|
2012-01-07 21:16:10 +00:00
|
|
|
// Vertical behavior.
|
|
|
|
if (parentAlign and GroupFlags_AlignTop) > 0 then
|
|
|
|
aRect.y := aRect.y + (parentGroup.bounds.y - (EvalBool(parentGroup.bounds.h > 0) * parentGroup.space) - newGroup^.margin - aRect.h)
|
|
|
|
else
|
|
|
|
aRect.y := aRect.y + (parentGroup.bounds.y + parentGroup.bounds.h + EvalBool(parentGroup.bounds.h > 0) * parentGroup.space + newGroup^.margin);
|
|
|
|
end
|
2011-08-15 22:08:43 +00:00
|
|
|
else
|
2012-01-07 21:16:10 +00:00
|
|
|
if parentLayout = GroupFlags_LayoutHorizontal then
|
|
|
|
begin
|
|
|
|
// Horizontal behavior.
|
|
|
|
if (parentAlign and GroupFlags_AlignRight) > 0 then
|
|
|
|
aRect.x := aRect.x + (parentGroup.bounds.x - (EvalBool(parentGroup.bounds.w > 0) * parentGroup.space) - newGroup^.margin - aRect.w)
|
|
|
|
else
|
|
|
|
aRect.x := aRect.x + (parentGroup.bounds.x + parentGroup.bounds.w + EvalBool(parentGroup.bounds.w > 0) * parentGroup.space + newGroup^.margin);
|
|
|
|
|
|
|
|
// Vertical behavior.
|
|
|
|
aRect.y := aRect.y + (parentGroup.bounds.y + newGroup^.margin + EvalBool((parentAlign and GroupFlags_AlignTop) > 0) * (parentGroup.bounds.h - 2 * newGroup^.margin - aRect.h));
|
|
|
|
end;
|
2011-08-15 22:08:43 +00:00
|
|
|
|
|
|
|
newGroup^.bounds := aRect;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure UIContext.beginGroup(groupFlags: integer);
|
|
|
|
var
|
|
|
|
r: Rect;
|
|
|
|
begin
|
|
|
|
r.Rect(0, 0, 0, 0);
|
|
|
|
beginGroup(groupFlags, r);
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure UIContext.endGroup;
|
|
|
|
var
|
|
|
|
newGroup: Group;
|
|
|
|
parentGroup: PGroup;
|
|
|
|
maxBoundX: integer;
|
|
|
|
minBoundX: integer;
|
|
|
|
maxBoundY: integer;
|
|
|
|
minBoundY: integer;
|
|
|
|
begin
|
|
|
|
// Pop the new group.
|
|
|
|
newGroup := m_groupStack[m_groupIndex];
|
|
|
|
Dec(m_groupIndex);
|
|
|
|
parentGroup := @m_groupStack[m_groupIndex];
|
|
|
|
|
|
|
|
// add any increment from the embedded group
|
|
|
|
if (parentGroup^.flags and (GroupFlags_LayoutVertical or GroupFlags_LayoutHorizontal)) > 0 then
|
|
|
|
begin
|
|
|
|
maxBoundX := max(parentGroup^.bounds.x + parentGroup^.bounds.w, newGroup.bounds.x + newGroup.bounds.w + newGroup.margin);
|
|
|
|
minBoundX := min(parentGroup^.bounds.x, newGroup.bounds.x - newGroup.margin);
|
|
|
|
parentGroup^.bounds.x := minBoundX;
|
|
|
|
parentGroup^.bounds.w := maxBoundX - minBoundX;
|
|
|
|
maxBoundY := max(parentGroup^.bounds.y + parentGroup^.bounds.h, newGroup.bounds.y + newGroup.bounds.h + newGroup.margin);
|
|
|
|
minBoundY := min(parentGroup^.bounds.y, newGroup.bounds.y - newGroup.margin);
|
|
|
|
parentGroup^.bounds.y := minBoundY;
|
|
|
|
parentGroup^.bounds.h := maxBoundY - minBoundY;
|
|
|
|
end;
|
2012-01-03 00:01:04 +00:00
|
|
|
|
2012-01-10 17:09:26 +00:00
|
|
|
{$IFDEF DEBUG} Painter.drawDebugRect(newGroup.bounds); {$ENDIF}
|
2011-08-15 22:08:43 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
procedure UIContext.beginFrame(groupFlags: integer; const rect: Rect; style: integer);
|
|
|
|
begin
|
|
|
|
beginGroup(groupFlags, rect);
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure UIContext.endFrame;
|
|
|
|
begin
|
|
|
|
endGroup;
|
2012-01-10 17:09:26 +00:00
|
|
|
Painter.drawFrame(m_groupStack[m_groupIndex + 1].bounds, m_groupStack[m_groupIndex + 1].margin, 0);
|
2011-08-15 22:08:43 +00:00
|
|
|
end;
|
|
|
|
|
2012-01-07 21:16:10 +00:00
|
|
|
function UIContext.beginPanel(var r: Rect; const Text: string; var isUnfold: boolean; groupFlags: integer; style: integer): boolean;
|
2011-08-15 22:08:43 +00:00
|
|
|
var
|
|
|
|
rt: Rect;
|
|
|
|
ra: Rect;
|
|
|
|
rpanel: Rect;
|
|
|
|
aRect: Rect;
|
|
|
|
focus: boolean;
|
|
|
|
hover: boolean;
|
|
|
|
tmp: Rect;
|
|
|
|
begin
|
2012-01-10 17:09:26 +00:00
|
|
|
rpanel := Painter.getPanelRect(SetRect(r.x, r.y), Text, rt, ra);
|
2012-01-07 21:16:10 +00:00
|
|
|
|
2011-08-15 22:08:43 +00:00
|
|
|
if (groupFlags and GroupFlags_LayoutDefault) > 0 then
|
|
|
|
groupFlags := GroupFlags_LayoutDefaultFallback;
|
|
|
|
|
|
|
|
beginGroup((groupFlags or GroupFlags_LayoutNoMargin or GroupFlags_LayoutNoSpace) and GroupFlags_StartXMask, rpanel);
|
2012-01-07 21:16:10 +00:00
|
|
|
|
2011-08-15 22:08:43 +00:00
|
|
|
aRect := m_groupStack[m_groupIndex].bounds;
|
2012-01-07 21:16:10 +00:00
|
|
|
|
2011-08-15 22:08:43 +00:00
|
|
|
focus := hasFocus(aRect);
|
|
|
|
hover := isHover(aRect);
|
2012-01-07 21:16:10 +00:00
|
|
|
|
2011-08-15 22:08:43 +00:00
|
|
|
if focus then
|
|
|
|
begin
|
|
|
|
m_uiOnFocus := True;
|
|
|
|
r.x := r.x + (m_currentCursor.x - m_mouseButton[0].cursor.x);
|
|
|
|
r.y := r.y + (m_currentCursor.y - m_mouseButton[0].cursor.y);
|
|
|
|
aRect.x := aRect.x + (m_currentCursor.x - m_mouseButton[0].cursor.x);
|
|
|
|
aRect.y := aRect.y + (m_currentCursor.y - m_mouseButton[0].cursor.y);
|
|
|
|
m_mouseButton[0].cursor := m_currentCursor;
|
|
|
|
end;
|
|
|
|
|
|
|
|
if ((m_mouseButton[0].state and ButtonFlags_End) > 0) and focus and (overlap(SetRect(aRect.x + ra.x, aRect.y + ra.y, ra.w, ra.h), m_currentCursor)) then
|
|
|
|
isUnfold := not isUnfold;
|
|
|
|
|
2012-01-10 17:09:26 +00:00
|
|
|
Painter.drawPanel(aRect, Text, rt, ra, isUnfold, hover, focus, style);
|
2011-08-15 22:08:43 +00:00
|
|
|
|
|
|
|
if isUnfold then
|
|
|
|
begin
|
|
|
|
tmp.Rect(0, 0, r.w, r.h);
|
|
|
|
beginFrame(groupFlags, tmp);
|
|
|
|
Result := True;
|
|
|
|
end
|
|
|
|
else
|
|
|
|
begin
|
|
|
|
endGroup;
|
|
|
|
Result := False;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure UIContext.endPanel;
|
|
|
|
begin
|
|
|
|
endFrame;
|
|
|
|
endGroup;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function UIContext.getGroupWidth: integer;
|
|
|
|
begin
|
|
|
|
Result := m_groupStack[m_groupIndex].bounds.w;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function UIContext.getGroupHeight: integer;
|
|
|
|
begin
|
|
|
|
Result := m_groupStack[m_groupIndex].bounds.h;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function UIContext.getCursorX: integer;
|
|
|
|
begin
|
|
|
|
Result := m_currentCursor.x;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function UIContext.getCursorY: integer;
|
|
|
|
begin
|
|
|
|
Result := m_currentCursor.y;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function UIContext.getMouseState(button: integer): ButtonState;
|
|
|
|
begin
|
|
|
|
Result := m_mouseButton[button];
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure UIContext.doTextureView(const aRect: Rect; const texID: integer; zoomRect: Rect; mipLevel: integer; texelScale: double; texelOffset: double; red: integer; green: integer; blue: integer; alpha: integer; style: integer);
|
|
|
|
var
|
|
|
|
rt: Rect;
|
|
|
|
rr: Rect;
|
|
|
|
begin
|
2012-01-10 17:09:26 +00:00
|
|
|
rr := placeRect(Painter.getTextureViewRect(aRect, rt));
|
2011-08-15 22:08:43 +00:00
|
|
|
if (zoomRect.w = 0) or (zoomRect.h = 0) then
|
|
|
|
zoomRect.Rect(0, 0, rt.w, rt.h);
|
|
|
|
|
2012-01-10 17:09:26 +00:00
|
|
|
Painter.drawTextureView(rr, texID, rt, zoomRect, mipLevel, texelScale, texelOffset, red, green, blue, alpha, style);
|
2011-08-15 22:08:43 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
function UIContext.getPainter: UIPainter;
|
|
|
|
begin
|
2012-01-10 17:09:26 +00:00
|
|
|
Result := Painter;
|
2011-08-15 22:08:43 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
function UIContext.window: Rect;
|
|
|
|
begin
|
|
|
|
Result := m_window;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure UIContext.setCursor(x: integer; y: integer);
|
|
|
|
begin
|
|
|
|
m_currentCursor.x := x;
|
|
|
|
m_currentCursor.y := m_window.h - y;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function UIContext.overlap(const aRect: Rect; const p: Point): boolean;
|
|
|
|
begin
|
|
|
|
Result := (p.x >= aRect.x) and (p.x < aRect.x + aRect.w) and
|
|
|
|
(p.y >= aRect.y) and (p.y < aRect.y + aRect.h);
|
|
|
|
end;
|
|
|
|
|
|
|
|
function UIContext.hasFocus(const aRect: Rect): boolean;
|
|
|
|
begin
|
|
|
|
if m_twoStepFocus then
|
|
|
|
Result := overlap(aRect, m_focusPoint)
|
|
|
|
else
|
|
|
|
Result := ((m_mouseButton[0].state and ButtonFlags_On) > 0) and overlap(aRect, m_mouseButton[0].cursor);
|
|
|
|
end;
|
|
|
|
|
|
|
|
function UIContext.isHover(const aRect: Rect): boolean;
|
|
|
|
begin
|
|
|
|
if m_uiOnFocus and not hasFocus(aRect) then
|
|
|
|
Result := False
|
|
|
|
else
|
|
|
|
Result := overlap(aRect, m_currentCursor);
|
|
|
|
end;
|
|
|
|
|
2012-01-10 17:09:26 +00:00
|
|
|
procedure UIContext.SetPainter(AValue: UIPainter);
|
|
|
|
begin
|
|
|
|
if FPainter=AValue then
|
|
|
|
exit;
|
|
|
|
|
|
|
|
FPainter:=AValue;
|
|
|
|
end;
|
|
|
|
|
2011-08-15 22:08:43 +00:00
|
|
|
function UIContext.placeRect(const r: Rect): Rect;
|
|
|
|
var
|
|
|
|
aGroup: PGroup;
|
|
|
|
aRect: Rect;
|
|
|
|
layout: integer;
|
|
|
|
alignment: integer;
|
|
|
|
minBoundX: integer;
|
|
|
|
minBoundY: integer;
|
|
|
|
begin
|
|
|
|
aGroup := @m_groupStack[m_groupIndex];
|
|
|
|
aRect := r;
|
|
|
|
layout := aGroup^.flags and GroupFlags_LayoutMask;
|
|
|
|
alignment := aGroup^.flags and GroupFlags_AlignMask;
|
|
|
|
|
|
|
|
if layout = GroupFlags_LayoutNone then
|
|
|
|
begin
|
|
|
|
// Translate rect to absolute coordinates.
|
|
|
|
aRect.x := aRect.x + (aGroup^.bounds.x);
|
|
|
|
aRect.y := aRect.y + (aGroup^.bounds.y);
|
|
|
|
end
|
|
|
|
else
|
|
|
|
if layout = GroupFlags_LayoutVertical then
|
|
|
|
begin
|
|
|
|
// Vertical behavior
|
|
|
|
if (alignment and GroupFlags_AlignTop) > 0 then
|
|
|
|
begin
|
|
|
|
// Move down bounds.y with the space for the new rect
|
|
|
|
aGroup^.bounds.y -= EvalBool(aGroup^.bounds.h > 0) * aGroup^.space + aRect.h;
|
|
|
|
|
|
|
|
// Widget's y is the group bounds.y
|
|
|
|
aRect.y := aGroup^.bounds.y;
|
|
|
|
end
|
|
|
|
else
|
|
|
|
aRect.y := aGroup^.bounds.y + aGroup^.bounds.h + EvalBool(aGroup^.bounds.h > 0) * aGroup^.space;
|
|
|
|
|
|
|
|
// Add space after first object inserted in the group
|
|
|
|
aGroup^.bounds.h += EvalBool(aGroup^.bounds.h > 0) * aGroup^.space + aRect.h;
|
|
|
|
|
|
|
|
// Horizontal behavior
|
|
|
|
if (alignment and GroupFlags_AlignRight) > 0 then
|
|
|
|
begin
|
|
|
|
aRect.x := aRect.x + (aGroup^.bounds.x + aGroup^.bounds.w - aRect.w);
|
|
|
|
minBoundX := min(aGroup^.bounds.x, aRect.x);
|
|
|
|
aGroup^.bounds.w := aGroup^.bounds.x + aGroup^.bounds.w - minBoundX;
|
|
|
|
aGroup^.bounds.x := minBoundX;
|
|
|
|
end
|
|
|
|
else
|
|
|
|
begin
|
|
|
|
aGroup^.bounds.w := max(aGroup^.bounds.w, aRect.x + aRect.w);
|
|
|
|
aRect.x := aRect.x + (aGroup^.bounds.x);
|
|
|
|
end;
|
|
|
|
end
|
|
|
|
else
|
|
|
|
if (layout = GroupFlags_LayoutHorizontal) then
|
|
|
|
begin
|
|
|
|
// Horizontal behavior
|
|
|
|
if (alignment and GroupFlags_AlignRight) > 0 then
|
|
|
|
begin
|
|
|
|
// Move left bounds.x with the space for the new rect
|
|
|
|
aGroup^.bounds.x -= EvalBool(aGroup^.bounds.w > 0) * aGroup^.space + aRect.w;
|
|
|
|
|
|
|
|
// Widget's x is the group bounds.x
|
|
|
|
aRect.x := aGroup^.bounds.x;
|
|
|
|
end
|
|
|
|
else
|
|
|
|
aRect.x := aGroup^.bounds.x + aGroup^.bounds.w + EvalBool(aGroup^.bounds.w > 0) * aGroup^.space;
|
|
|
|
|
|
|
|
// Add space after first object inserted in the group
|
|
|
|
aGroup^.bounds.w += EvalBool(aGroup^.bounds.w > 0) * aGroup^.space + aRect.w;
|
|
|
|
|
|
|
|
// Vertical behavior
|
|
|
|
if (alignment and GroupFlags_AlignTop) > 0 then
|
|
|
|
begin
|
|
|
|
aRect.y := aRect.y + (aGroup^.bounds.y + aGroup^.bounds.h - aRect.h);
|
|
|
|
minBoundY := min(aGroup^.bounds.y, aRect.y);
|
|
|
|
aGroup^.bounds.h := aGroup^.bounds.y + aGroup^.bounds.h - minBoundY;
|
|
|
|
aGroup^.bounds.y := minBoundY;
|
|
|
|
end
|
|
|
|
else
|
|
|
|
begin
|
|
|
|
aGroup^.bounds.h := max(aGroup^.bounds.h, aRect.y + aRect.h);
|
|
|
|
aRect.y := aRect.y + (aGroup^.bounds.y);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
Result := aRect;
|
|
|
|
end;
|
|
|
|
|
|
|
|
{ UIPainter }
|
|
|
|
|
2012-01-10 17:09:26 +00:00
|
|
|
procedure UIPainter.SetFont(AValue: TnvBaseFont);
|
|
|
|
begin
|
|
|
|
if FFont=AValue then Exit;
|
|
|
|
FFont:=AValue;
|
|
|
|
end;
|
|
|
|
|
2011-08-15 22:08:43 +00:00
|
|
|
constructor UIPainter.Create;
|
|
|
|
begin
|
|
|
|
inherited;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure UIPainter._begin(const window: Rect);
|
|
|
|
begin
|
|
|
|
init;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure UIPainter._end;
|
|
|
|
begin
|
|
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
function UIPainter.getCanvasMargin: integer;
|
|
|
|
begin
|
|
|
|
Result := 0;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function UIPainter.getCanvasSpace: integer;
|
|
|
|
begin
|
|
|
|
Result := 0;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function UIPainter.getFontHeight: integer;
|
|
|
|
begin
|
|
|
|
Result := 0;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function UIPainter.getTextLineWidth(const Text: string): integer;
|
|
|
|
begin
|
|
|
|
Result := 0;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function UIPainter.getTextSize(const Text: string; out nbLines: integer): integer;
|
|
|
|
begin
|
|
|
|
Result := 0;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function UIPainter.getPickedCharNb(const Text: string; const at: Point): integer;
|
|
|
|
begin
|
|
|
|
Result := 0;
|
|
|
|
end;
|
|
|
|
|
|
|
|
end.
|
|
|
|
|