sync: restore source from Google Drive
Sync changed files to Google Drive / sync (push) Successful in 1h45m13s
Sync changed files to Google Drive / sync (push) Successful in 1h45m13s
This commit is contained in:
1 parent
e2d8247e91
commit
7063d1b1c9
993 files changed
+949976
No files matched your search
+2138
File diff suppressed because it is too large.
Load diff
+848
@@ -0,0 +1,848 @@
|
||||
unit KM_ControlsBase;
|
||||
{$I KaM_Remake.inc}
|
||||
interface
|
||||
uses
|
||||
Classes, Controls,
|
||||
KromOGLUtils,
|
||||
KM_Controls,
|
||||
KM_RenderUI,
|
||||
KM_ResFonts, KM_ResTypes,
|
||||
KM_CommonTypes, KM_Points;
|
||||
|
||||
|
||||
type
|
||||
// Beveled area
|
||||
TKMBevel = class(TKMControl)
|
||||
const
|
||||
DEF_BACK_ALPHA = 0.4;
|
||||
DEF_EDGE_ALPHA = 0.75;
|
||||
public
|
||||
BackAlpha: Single;
|
||||
EdgeAlpha: Single;
|
||||
Color: TKMColor3f;
|
||||
constructor Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer);
|
||||
|
||||
procedure SetDefBackAlpha;
|
||||
procedure SetDefEdgeAlpha;
|
||||
procedure SetDefColor;
|
||||
|
||||
procedure Paint; override;
|
||||
end;
|
||||
|
||||
|
||||
// Rectangle area
|
||||
TKMShape = class(TKMControl)
|
||||
public
|
||||
FillColor: TColor4;
|
||||
LineColor: TColor4; //color of outline
|
||||
LineWidth: Byte;
|
||||
public
|
||||
constructor Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer);
|
||||
procedure Paint; override;
|
||||
end;
|
||||
|
||||
|
||||
// Text Label
|
||||
TKMLabel = class(TKMControl)
|
||||
private
|
||||
fWordWrap: Boolean;
|
||||
fFont: TKMFont;
|
||||
fFontColor: TColor4; //Usually white (self-colored)
|
||||
fCaption: UnicodeString; //Original text
|
||||
fText: UnicodeString; //Reformatted text
|
||||
fTextAlign: TKMTextAlign;
|
||||
fTextVAlign: TKMTextVAlign;
|
||||
fTextSize: TKMPoint;
|
||||
fStrikethrough: Boolean;
|
||||
fTabWidth: Integer;
|
||||
|
||||
procedure SetCaption(const aCaption: UnicodeString);
|
||||
procedure SetWordWrap(aValue: Boolean);
|
||||
procedure ReformatText;
|
||||
procedure SetFont(const Value: TKMFont);
|
||||
protected
|
||||
procedure SetWidth(aValue: Integer); override;
|
||||
|
||||
function GetIsPainted: Boolean; override;
|
||||
public
|
||||
MaxLines: Integer;
|
||||
|
||||
constructor Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; const aCaption: UnicodeString;
|
||||
aFont: TKMFont; aTextAlign: TKMTextAlign); overload;
|
||||
constructor Create(aParent: TKMPanel; aLeft,aTop: Integer; const aCaption: UnicodeString; aFont: TKMFont;
|
||||
aTextAlign: TKMTextAlign); overload;
|
||||
|
||||
function HitTest(X, Y: Integer; aIncludeDisabled: Boolean = False; aIncludeNotHitable: Boolean = False): Boolean; override;
|
||||
procedure SetColor(aColor: Cardinal);
|
||||
|
||||
property WordWrap: Boolean read fWordWrap write SetWordWrap; //Whether to automatically wrap text within given text area width
|
||||
property Caption: UnicodeString read fCaption write SetCaption;
|
||||
property FontColor: TColor4 read fFontColor write fFontColor;
|
||||
property Strikethrough: Boolean read fStrikethrough write fStrikethrough;
|
||||
property TabWidth: Integer read fTabWidth write fTabWidth;
|
||||
property TextSize: TKMPoint read fTextSize;
|
||||
function TextLeft: Integer;
|
||||
property TextVAlign: TKMTextVAlign read fTextVAlign write fTextVAlign;
|
||||
property Font: TKMFont read fFont write SetFont;
|
||||
procedure Paint; override;
|
||||
end;
|
||||
|
||||
|
||||
// Label that is scrolled within an area. Used in Credits
|
||||
TKMLabelScroll = class(TKMLabel)
|
||||
public
|
||||
SmoothScrollToTop: Cardinal; //Delta between this and TimeGetTime affects vertical position
|
||||
constructor Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; const aCaption: UnicodeString; aFont: TKMFont; aTextAlign: TKMTextAlign);
|
||||
procedure Paint; override;
|
||||
end;
|
||||
|
||||
|
||||
// Image
|
||||
TKMImage = class(TKMControl)
|
||||
private
|
||||
fRX: TRXType;
|
||||
fTexID: Word;
|
||||
fFlagColor: TColor4;
|
||||
protected
|
||||
function GetIsPainted: Boolean; override;
|
||||
public
|
||||
ImageAnchors: TKMAnchorsSet;
|
||||
Highlight: Boolean;
|
||||
HighlightOnMouseOver: Boolean;
|
||||
HighlightCoef: Single;
|
||||
Lightness: Single;
|
||||
ClipToBounds: Boolean;
|
||||
Tiled: Boolean;
|
||||
constructor Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; aTexID: Word; aRX: TRXType = rxGui;
|
||||
aImageAnchors: TKMAnchorsSet = [anLeft, anTop]);
|
||||
property RX: TRXType read fRX write fRX;
|
||||
property TexID: Word read fTexID write fTexID;
|
||||
property FlagColor: TColor4 read fFlagColor write fFlagColor;
|
||||
function Click: Boolean;
|
||||
procedure ImageStretch;
|
||||
procedure ImageCenter;
|
||||
procedure Paint; override;
|
||||
end;
|
||||
|
||||
|
||||
// Image stack - for army formation view
|
||||
TKMImageStack = class(TKMControl)
|
||||
private
|
||||
fRX: TRXType;
|
||||
fTexID1, fTexID2: Word; //Normal and commander
|
||||
fCount: Integer;
|
||||
fColumns: Integer;
|
||||
fDrawWidth: Integer;
|
||||
fDrawHeight: Integer;
|
||||
fHighlightID: Integer;
|
||||
public
|
||||
constructor Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; aTexID1, aTexID2: Word; aRX: TRXType = rxGui);
|
||||
procedure SetCount(aCount, aColumns, aHighlightID: Word);
|
||||
procedure Paint; override;
|
||||
end;
|
||||
|
||||
|
||||
// 3D Button
|
||||
TKMButton = class(TKMControl)
|
||||
private
|
||||
fCaption: UnicodeString;
|
||||
fTextAlign: TKMTextAlign;
|
||||
fStyle: TKMButtonStyle;
|
||||
fRX: TRXType;
|
||||
fAutoHeight: Boolean; //Set button height automatically depending text size (height)
|
||||
procedure InitCommon(aStyle: TKMButtonStyle);
|
||||
procedure SetCaption(const aCaption: UnicodeString);
|
||||
procedure SetAutoHeight(aValue: Boolean);
|
||||
procedure UpdateHeight;
|
||||
public
|
||||
FlagColor: TColor4; //When using an image
|
||||
CapColor: TColor4;
|
||||
Font: TKMFont;
|
||||
MakesSound: Boolean;
|
||||
TexID: Word;
|
||||
CapOffsetX: Shortint;
|
||||
CapOffsetY: Shortint;
|
||||
ShowImageEnabled: Boolean; // show picture as enabled or not (normal or darkened)
|
||||
TextVAlign: TKMTextVAlign;
|
||||
AutoTextPadding: Byte; //text padding for autoHeight
|
||||
constructor Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; aTexID: Word; aRX: TRXType;
|
||||
aStyle: TKMButtonStyle); overload;
|
||||
constructor Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; const aCaption: UnicodeString;
|
||||
aStyle: TKMButtonStyle); overload;
|
||||
function Click: Boolean; //Try to click a button and return TRUE if succeded
|
||||
|
||||
property Caption: UnicodeString read fCaption write SetCaption;
|
||||
property AutoHeight: Boolean read fAutoHeight write SetAutoHeight;
|
||||
|
||||
procedure MouseUp(X,Y: Integer; Shift: TShiftState; Button: TMouseButton); override;
|
||||
procedure Paint; override;
|
||||
end;
|
||||
|
||||
|
||||
// Common Flat Button
|
||||
TKMButtonFlatCommon = class abstract(TKMControl)
|
||||
private
|
||||
public
|
||||
RX: TRXType;
|
||||
TexID: Word;
|
||||
TexOffsetX: Shortint;
|
||||
TexOffsetY: Shortint;
|
||||
CapOffsetX: Shortint;
|
||||
CapOffsetY: Shortint;
|
||||
Caption: UnicodeString;
|
||||
CapColor: TColor4;
|
||||
FlagColor: TColor4;
|
||||
Font: TKMFont;
|
||||
HideHighlight: Boolean;
|
||||
Clickable: Boolean; //Disables clicking without dimming
|
||||
EdgeAlpha: Single;
|
||||
BackAlpha: Single;
|
||||
|
||||
constructor Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight, aTexID: Integer; aRX: TRXType = rxGui);
|
||||
|
||||
procedure MouseUp(X,Y: Integer; Shift: TShiftState; Button: TMouseButton); override;
|
||||
|
||||
procedure Paint; override;
|
||||
end;
|
||||
|
||||
|
||||
// FlatButton
|
||||
TKMButtonFlat = class(TKMButtonFlatCommon)
|
||||
public
|
||||
Down: Boolean;
|
||||
procedure Paint; override;
|
||||
end;
|
||||
|
||||
|
||||
// FlatButton with Shape on it
|
||||
TKMFlatButtonShape = class(TKMControl)
|
||||
private
|
||||
fCaption: UnicodeString;
|
||||
fFont: TKMFont;
|
||||
fFontHeight: Byte;
|
||||
public
|
||||
Down: Boolean;
|
||||
FontColor: TColor4;
|
||||
ShapeColor: TColor4;
|
||||
constructor Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; const aCaption: UnicodeString; aFont: TKMFont; aShapeColor: TColor4);
|
||||
procedure Paint; override;
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
uses
|
||||
{$IFDEF MSWindows} Windows, {$ENDIF}
|
||||
{$IFDEF Unix} LCLIntf, LCLType, {$ENDIF}
|
||||
StrUtils, Math,
|
||||
KM_ControlsTypes,
|
||||
KM_Sound,
|
||||
KM_Resource, KM_ResSound, KM_ResSprites,
|
||||
KM_Defaults, KM_CommonUtils,
|
||||
KM_RenderTypes;
|
||||
|
||||
|
||||
{ TKMBevel }
|
||||
constructor TKMBevel.Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer);
|
||||
begin
|
||||
inherited Create(aParent, aLeft, aTop, aWidth, aHeight);
|
||||
|
||||
SetDefBackAlpha;
|
||||
SetDefEdgeAlpha;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMBevel.SetDefBackAlpha;
|
||||
begin
|
||||
BackAlpha := DEF_BACK_ALPHA; //Default value
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMBevel.SetDefEdgeAlpha;
|
||||
begin
|
||||
EdgeAlpha := DEF_EDGE_ALPHA; //Default value
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMBevel.SetDefColor;
|
||||
begin
|
||||
Color := COLOR3F_BLACK; //Default value
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMBevel.Paint;
|
||||
begin
|
||||
inherited;
|
||||
TKMRenderUI.WriteBevel(AbsLeft, AbsTop, Width, Height, Color, EdgeAlpha, BackAlpha);
|
||||
end;
|
||||
|
||||
|
||||
{ TKMShape }
|
||||
constructor TKMShape.Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer);
|
||||
begin
|
||||
inherited Create(aParent, aLeft, aTop, aWidth, aHeight);
|
||||
|
||||
LineWidth := 2;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMShape.Paint;
|
||||
begin
|
||||
inherited;
|
||||
TKMRenderUI.WriteShape(AbsLeft, AbsTop, Width, Height, FillColor);
|
||||
TKMRenderUI.WriteOutline(AbsLeft, AbsTop, Width, Height, LineWidth, LineColor);
|
||||
end;
|
||||
|
||||
|
||||
{ TKMLabel }
|
||||
constructor TKMLabel.Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; const aCaption: UnicodeString;
|
||||
aFont: TKMFont; aTextAlign: TKMTextAlign);
|
||||
begin
|
||||
inherited Create(aParent, aLeft, aTop, aWidth, aHeight);
|
||||
fFont := aFont;
|
||||
fFontColor := $FFFFFFFF;
|
||||
fTextAlign := aTextAlign;
|
||||
fTextVAlign := tvaTop;
|
||||
fWordWrap := False;
|
||||
fTabWidth := FONT_TAB_WIDTH;
|
||||
SetCaption(aCaption);
|
||||
end;
|
||||
|
||||
|
||||
//Same as above but with width/height ommitted, as in most cases we don't know/don't care
|
||||
constructor TKMLabel.Create(aParent: TKMPanel; aLeft, aTop: Integer; const aCaption: UnicodeString; aFont: TKMFont;
|
||||
aTextAlign: TKMTextAlign);
|
||||
begin
|
||||
Create(aParent, aLeft, aTop, 0, 0, aCaption, aFont, aTextAlign);
|
||||
end;
|
||||
|
||||
|
||||
function TKMLabel.TextLeft: Integer;
|
||||
begin
|
||||
case fTextAlign of
|
||||
taLeft: Result := AbsLeft;
|
||||
taCenter: Result := AbsLeft + Round((Width - fTextSize.X) / 2);
|
||||
taRight: Result := AbsLeft + (Width - fTextSize.X);
|
||||
else Result := AbsLeft;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMLabel.SetCaption(const aCaption: UnicodeString);
|
||||
begin
|
||||
fCaption := aCaption;
|
||||
ReformatText;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMLabel.SetWordWrap(aValue: Boolean);
|
||||
begin
|
||||
fWordWrap := aValue;
|
||||
ReformatText;
|
||||
end;
|
||||
|
||||
|
||||
//Override usual hittest with regard to text alignment
|
||||
function TKMLabel.HitTest(X, Y: Integer; aIncludeDisabled: Boolean = False; aIncludeNotHitable: Boolean = False): Boolean;
|
||||
begin
|
||||
Result := (Hitable or aIncludeNotHitable)
|
||||
and InRange(X, TextLeft, TextLeft + fTextSize.X)
|
||||
and InRange(Y, AbsTop, AbsTop + Height);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMLabel.SetColor(aColor: Cardinal);
|
||||
begin
|
||||
fCaption := StripColor(fCaption);
|
||||
Caption := WrapColor(fCaption, aColor);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMLabel.SetFont(const Value: TKMFont);
|
||||
begin
|
||||
fFont := Value;
|
||||
ReformatText;
|
||||
end;
|
||||
|
||||
|
||||
// Existing EOLs should be preserved, and new ones added where needed
|
||||
// Keep original intact incase we need to Reformat text once again
|
||||
procedure TKMLabel.ReformatText;
|
||||
var
|
||||
I: Integer;
|
||||
stake: Integer;
|
||||
begin
|
||||
if fWordWrap then
|
||||
fText := gRes.Fonts[fFont].WordWrap(fCaption, Width, True, False)
|
||||
else
|
||||
fText := fCaption;
|
||||
|
||||
// We may need to display only N lines
|
||||
if MaxLines > 0 then
|
||||
begin
|
||||
stake := 0;
|
||||
for I := 1 to MaxLines do
|
||||
begin
|
||||
stake := PosEx(#124, fText, stake+1);
|
||||
if stake = 0 then
|
||||
Break;
|
||||
end;
|
||||
|
||||
if stake > 0 then
|
||||
fText := LeftStr(fText, stake - 1);
|
||||
end;
|
||||
|
||||
fTextSize := gRes.Fonts[fFont].GetTextSize(fText);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMLabel.SetWidth(aValue: Integer);
|
||||
begin
|
||||
inherited;
|
||||
|
||||
if fWordWrap then
|
||||
ReformatText;
|
||||
end;
|
||||
|
||||
|
||||
function TKMLabel.GetIsPainted: Boolean;
|
||||
begin
|
||||
Result := inherited and (Length(fCaption) > 0 );
|
||||
end;
|
||||
|
||||
|
||||
// Send caption to render
|
||||
procedure TKMLabel.Paint;
|
||||
var
|
||||
t: Integer;
|
||||
col: Cardinal;
|
||||
begin
|
||||
inherited;
|
||||
|
||||
if Enabled then
|
||||
col := FontColor
|
||||
else
|
||||
col := $FF888888;
|
||||
|
||||
t := 0;
|
||||
if Height > 0 then
|
||||
begin
|
||||
case fTextVAlign of
|
||||
tvaNone,
|
||||
tvaTop: ;
|
||||
tvaMiddle: t := (Height - fTextSize.Y) div 2;
|
||||
tvaBottom: t := Height - fTextSize.Y;
|
||||
end;
|
||||
end;
|
||||
|
||||
TKMRenderUI.WriteText(AbsLeft, AbsTop + t, Width, fText, fFont, fTextAlign, col, False, False, False, fTabWidth);
|
||||
|
||||
if fStrikethrough then
|
||||
TKMRenderUI.WriteShape(TextLeft, AbsTop + fTextSize.Y div 2 - 2, fTextSize.X, 3, col, $FF000000);
|
||||
end;
|
||||
|
||||
|
||||
{ TKMLabelScroll }
|
||||
constructor TKMLabelScroll.Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; const aCaption: UnicodeString; aFont: TKMFont; aTextAlign: TKMTextAlign);
|
||||
begin
|
||||
inherited Create(aParent, aLeft, aTop, aWidth, aHeight, aCaption, aFont, aTextAlign);
|
||||
SmoothScrollToTop := 0; //Disabled by default
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMLabelScroll.Paint;
|
||||
var
|
||||
newTop: Integer;
|
||||
col: Cardinal;
|
||||
begin
|
||||
TKMRenderUI.SetupClipY(AbsTop, AbsTop + Height);
|
||||
newTop := EnsureRange(AbsTop + Height - TimeSince(SmoothScrollToTop) div 50, -MINSHORT, MAXSHORT); //Compute delta and shift by it upwards (Credits page)
|
||||
|
||||
if Enabled then
|
||||
col := FontColor
|
||||
else
|
||||
col := $FF888888;
|
||||
|
||||
TKMRenderUI.WriteText(AbsLeft, newTop, Width, fCaption, fFont, fTextAlign, col);
|
||||
TKMRenderUI.ReleaseClipY;
|
||||
end;
|
||||
|
||||
|
||||
{ TKMImage }
|
||||
constructor TKMImage.Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; aTexID: Word; aRX: TRXType = rxGui;
|
||||
aImageAnchors: TKMAnchorsSet = [anLeft, anTop]);
|
||||
begin
|
||||
inherited Create(aParent, aLeft, aTop, aWidth, aHeight);
|
||||
fRX := aRX;
|
||||
fTexID := aTexID;
|
||||
fFlagColor := $FFFF00FF;
|
||||
ImageAnchors := aImageAnchors;
|
||||
Highlight := False;
|
||||
HighlightOnMouseOver := False;
|
||||
HighlightCoef := CTRL_HIGHLIGHT_COEF_DEF;
|
||||
end;
|
||||
|
||||
|
||||
function TKMImage.GetIsPainted: Boolean;
|
||||
begin
|
||||
Result := inherited and (TexID <> 0);
|
||||
end;
|
||||
|
||||
|
||||
//DoClick is called by keyboard shortcuts
|
||||
//It's important that Control must be:
|
||||
// IsVisible (can't shortcut invisible/unaccessible button)
|
||||
// Enabled (can't shortcut disabled function, e.g. Halt during fight)
|
||||
function TKMImage.Click: Boolean;
|
||||
begin
|
||||
if Visible and Enabled then
|
||||
begin
|
||||
//Mark self as CtrlOver and CtrlUp, don't mark CtrlDown since MouseUp manually Nils it
|
||||
Parent.MasterControl.CtrlOver := Self;
|
||||
Parent.MasterControl.CtrlUp := Self;
|
||||
if Assigned(OnClick) then OnClick(Self);
|
||||
Result := True; //Click has happened
|
||||
end
|
||||
else
|
||||
Result := False; //No, we couldn't click for Control is unreachable
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMImage.ImageStretch;
|
||||
begin
|
||||
ImageAnchors := [anLeft, anRight, anTop, anBottom]; //Stretch image to fit
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMImage.ImageCenter; //Render image from center
|
||||
begin
|
||||
ImageAnchors := [];
|
||||
end;
|
||||
|
||||
|
||||
{If image area is bigger than image - do center image in it}
|
||||
procedure TKMImage.Paint;
|
||||
var
|
||||
x, y: Integer;
|
||||
col, row: Integer;
|
||||
paintLightness: Single;
|
||||
drawLeft, drawTop: Integer;
|
||||
drawWidth, drawHeight: Integer;
|
||||
begin
|
||||
inherited;
|
||||
if fTexID = 0 then Exit; //No picture to draw
|
||||
|
||||
if ClipToBounds then
|
||||
begin
|
||||
TKMRenderUI.SetupClipX(AbsLeft, AbsLeft + Width);
|
||||
TKMRenderUI.SetupClipY(AbsTop, AbsTop + Height);
|
||||
end;
|
||||
|
||||
paintLightness := Lightness + HighlightCoef * (Byte(HighlightOnMouseOver and (csOver in State)) + Byte(Highlight));
|
||||
|
||||
if Tiled then
|
||||
begin
|
||||
drawWidth := gGFXData[fRX, fTexID].PxWidth;
|
||||
drawHeight := gGFXData[fRX, fTexID].PxHeight;
|
||||
drawLeft := AbsLeft + Width div 2 - drawWidth div 2;
|
||||
drawTop := AbsTop + Height div 2 - drawHeight div 2;
|
||||
|
||||
col := Width div drawWidth + 1;
|
||||
row := Height div drawHeight + 1;
|
||||
for x := -col div 2 to col div 2 do
|
||||
for y := -row div 2 to row div 2 do
|
||||
TKMRenderUI.WritePicture(drawLeft + x * drawWidth, drawTop + y * drawHeight, drawWidth, drawHeight, ImageAnchors, fRX, fTexID, Enabled, fFlagColor, paintLightness);
|
||||
end
|
||||
else
|
||||
TKMRenderUI.WritePicture(AbsLeft, AbsTop, Width, Height, ImageAnchors, fRX, fTexID, Enabled, fFlagColor, paintLightness);
|
||||
|
||||
if ClipToBounds then
|
||||
begin
|
||||
TKMRenderUI.ReleaseClipX;
|
||||
TKMRenderUI.ReleaseClipY;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
{ TKMImageStack }
|
||||
constructor TKMImageStack.Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; aTexID1, aTexID2: Word; aRX: TRXType = rxGui);
|
||||
begin
|
||||
inherited Create(aParent, aLeft, aTop, aWidth, aHeight);
|
||||
|
||||
fRX := aRX;
|
||||
fTexID1 := aTexID1;
|
||||
fTexID2 := aTexID2;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMImageStack.SetCount(aCount, aColumns, aHighlightID: Word);
|
||||
var
|
||||
aspect: Single;
|
||||
begin
|
||||
fCount := aCount;
|
||||
fColumns := Math.max(1, aColumns);
|
||||
fHighlightID := aHighlightID;
|
||||
|
||||
fDrawWidth := EnsureRange(Width div fColumns, 8, gGFXData[fRX, fTexID1].PxWidth);
|
||||
fDrawHeight := EnsureRange(Height div Ceil(fCount/fColumns), 6, gGFXData[fRX, fTexID1].PxHeight);
|
||||
|
||||
aspect := gGFXData[fRX, fTexID1].PxWidth / gGFXData[fRX, fTexID1].PxHeight;
|
||||
if fDrawHeight * aspect <= fDrawWidth then
|
||||
fDrawWidth := Round(fDrawHeight * aspect)
|
||||
else
|
||||
fDrawHeight := Round(fDrawWidth / aspect);
|
||||
end;
|
||||
|
||||
|
||||
// If image area is bigger than image - do center image in it
|
||||
procedure TKMImageStack.Paint;
|
||||
var
|
||||
I: Integer;
|
||||
offsetX, offsetY, centerX, centerY: SmallInt; //variable parameters
|
||||
texID: Word;
|
||||
begin
|
||||
inherited;
|
||||
if fTexID1 = 0 then Exit; //No picture to draw
|
||||
|
||||
offsetX := Width div fColumns;
|
||||
offsetY := Height div Ceil(fCount / fColumns);
|
||||
|
||||
centerX := (Width - offsetX * (fColumns-1) - fDrawWidth) div 2;
|
||||
centerY := (Height - offsetY * (Ceil(fCount/fColumns) - 1) - fDrawHeight) div 2;
|
||||
|
||||
for I := 0 to fCount - 1 do
|
||||
begin
|
||||
texID := IfThen(I = fHighlightID, fTexID2, fTexId1);
|
||||
|
||||
TKMRenderUI.WritePicture(AbsLeft + centerX + offsetX * (I mod fColumns),
|
||||
AbsTop + centerY + offsetY * (I div fColumns),
|
||||
fDrawWidth, fDrawHeight, [anLeft, anTop, anRight, anBottom], fRX, texID, Enabled);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
{ TKMButton }
|
||||
constructor TKMButton.Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; aTexID: Word; aRX: TRXType;
|
||||
aStyle: TKMButtonStyle);
|
||||
begin
|
||||
inherited Create(aParent, aLeft, aTop, aWidth, aHeight);
|
||||
|
||||
InitCommon(aStyle);
|
||||
fRX := aRX;
|
||||
TexID := aTexID;
|
||||
end;
|
||||
|
||||
|
||||
{Different version of button, with caption on it instead of image}
|
||||
constructor TKMButton.Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; const aCaption: UnicodeString;
|
||||
aStyle: TKMButtonStyle);
|
||||
begin
|
||||
inherited Create(aParent, aLeft, aTop, aWidth, aHeight);
|
||||
|
||||
InitCommon(aStyle);
|
||||
Caption := aCaption;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMButton.InitCommon(aStyle: TKMButtonStyle);
|
||||
begin
|
||||
TexID := 0;
|
||||
Caption := '';
|
||||
FlagColor := $FFFF00FF;
|
||||
Font := fntMetal;
|
||||
fTextAlign := taCenter; //Thats default everywhere in KaM
|
||||
TextVAlign := tvaMiddle;//tvaNone;
|
||||
fStyle := aStyle;
|
||||
MakesSound := True;
|
||||
ShowImageEnabled := True;
|
||||
AutoHeight := False;
|
||||
AutoTextPadding := 5;
|
||||
CapColor := icWhite;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMButton.UpdateHeight;
|
||||
var
|
||||
textY: Integer;
|
||||
begin
|
||||
if fAutoHeight then
|
||||
begin
|
||||
textY := gRes.Fonts[Font].GetTextSize(Caption).Y;
|
||||
if textY + AutoTextPadding > Height then
|
||||
Height := textY + AutoTextPadding;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMButton.SetCaption(const aCaption: UnicodeString);
|
||||
begin
|
||||
fCaption := aCaption;
|
||||
UpdateHeight;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMButton.SetAutoHeight(aValue: Boolean);
|
||||
begin
|
||||
fAutoHeight := aValue;
|
||||
UpdateHeight;
|
||||
end;
|
||||
|
||||
|
||||
//DoClick is called by keyboard shortcuts
|
||||
//It puts a focus on the button and depresses it if it was DoPress'ed
|
||||
//It's important that Control must be:
|
||||
// Visible (can't shortcut invisible/unaccessible button)
|
||||
// Enabled (can't shortcut disabled function, e.g. Halt during fight)
|
||||
function TKMButton.Click: Boolean;
|
||||
begin
|
||||
if Visible and Enabled then
|
||||
begin
|
||||
//Mark self as CtrlOver and CtrlUp, don't mark CtrlDown since MouseUp manually Nils it
|
||||
Parent.MasterControl.CtrlOver := Self;
|
||||
Parent.MasterControl.CtrlUp := Self;
|
||||
if Assigned(OnClick) then
|
||||
OnClick(Self);
|
||||
Result := True; //Click has happened
|
||||
end
|
||||
else
|
||||
Result := False; //No, we couldn't click for Control is unreachable
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMButton.MouseUp(X,Y: Integer; Shift: TShiftState; Button: TMouseButton);
|
||||
begin
|
||||
if Enabled and MakesSound and (csDown in State) then
|
||||
gSoundPlayer.Play(sfxnButtonClick);
|
||||
|
||||
inherited;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMButton.Paint;
|
||||
var
|
||||
col: TColor4;
|
||||
stateSet: TKMButtonStateSet;
|
||||
textY, top: Integer;
|
||||
begin
|
||||
inherited;
|
||||
stateSet := [];
|
||||
if (csOver in State) and Enabled then
|
||||
stateSet := stateSet + [bsOver];
|
||||
if (csOver in State) and (csDown in State) then
|
||||
stateSet := stateSet + [bsDown];
|
||||
if not Enabled then
|
||||
stateSet := stateSet + [bsDisabled];
|
||||
|
||||
TKMRenderUI.Write3DButton(AbsLeft, AbsTop, Width, Height, fRX, TexID, FlagColor, stateSet, fStyle, ShowImageEnabled);
|
||||
|
||||
if TexID <> 0 then Exit;
|
||||
|
||||
//If disabled then text should be faded
|
||||
col := IfThen(Enabled, CapColor, icGray);
|
||||
|
||||
top := AbsTop + Byte(bsDown in stateSet) + CapOffsetY;
|
||||
|
||||
textY := gRes.Fonts[Font].GetTextSize(Caption).Y;
|
||||
case TextVAlign of
|
||||
tvaNone: Inc(top, (Height div 2) - 7);
|
||||
tvaTop: Inc(top, 2);
|
||||
tvaMiddle: Inc(top, (Height div 2) - (textY div 2) + 2);
|
||||
tvaBottom: Inc(top, Height - textY);
|
||||
end;
|
||||
TKMRenderUI.WriteText(AbsLeft + Byte(bsDown in stateSet) + CapOffsetX, top,
|
||||
Width, Caption, Font, fTextAlign, col);
|
||||
end;
|
||||
|
||||
|
||||
{TKMButtonFlatCommon}
|
||||
constructor TKMButtonFlatCommon.Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight, aTexID: Integer; aRX: TRXType = rxGui);
|
||||
begin
|
||||
inherited Create(aParent, aLeft, aTop, aWidth, aHeight);
|
||||
RX := aRX;
|
||||
TexID := aTexID;
|
||||
FlagColor := $FFFF00FF;
|
||||
CapColor := $FFFFFFFF;
|
||||
Font := fntGame;
|
||||
Clickable := True;
|
||||
EdgeAlpha := BEVEL_EDGE_ALPHA_DEF;
|
||||
BackAlpha := BEVEL_BACK_ALPHA_DEF;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMButtonFlatCommon.MouseUp(X,Y: Integer; Shift: TShiftState; Button: TMouseButton);
|
||||
begin
|
||||
if not Clickable then Exit;
|
||||
if Enabled and (csDown in State) then
|
||||
gSoundPlayer.Play(sfxClick);
|
||||
|
||||
inherited;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMButtonFlatCommon.Paint;
|
||||
begin
|
||||
inherited;
|
||||
|
||||
TKMRenderUI.WriteBevel(AbsLeft, AbsTop, Width, Height, EdgeAlpha, BackAlpha);
|
||||
|
||||
if (csOver in State) and Enabled and not HideHighlight then
|
||||
TKMRenderUI.WriteShape(AbsLeft+1, AbsTop+1, Width-2, Height-2, $40FFFFFF);
|
||||
end;
|
||||
|
||||
|
||||
//Simple version of button, with a caption and image
|
||||
{TKMButtonFlat}
|
||||
procedure TKMButtonFlat.Paint;
|
||||
var
|
||||
textCol: TColor4;
|
||||
begin
|
||||
inherited;
|
||||
|
||||
if TexID <> 0 then
|
||||
TKMRenderUI.WritePicture(AbsLeft + TexOffsetX,
|
||||
AbsTop + TexOffsetY - 6 * Byte(Caption <> ''),
|
||||
Width, Height, [], RX, TexID, Enabled or fEnabledVisually, FlagColor);
|
||||
|
||||
textCol := IfThen(Enabled or fEnabledVisually, CapColor, icGray);
|
||||
TKMRenderUI.WriteText(AbsLeft + CapOffsetX, AbsTop + (Height div 2) + 4 + CapOffsetY, Width, Caption, Font, taCenter, textCol);
|
||||
|
||||
if Down then
|
||||
TKMRenderUI.WriteOutline(AbsLeft, AbsTop, Width, Height, 1, $FFFFFFFF);
|
||||
{if not fEnabled then
|
||||
TKMRenderUI.WriteShape(Left, Top, Width, Height, $80000000);}
|
||||
end;
|
||||
|
||||
|
||||
{ TKMFlatButtonShape }
|
||||
constructor TKMFlatButtonShape.Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; const aCaption: UnicodeString; aFont: TKMFont; aShapeColor: TColor4);
|
||||
begin
|
||||
inherited Create(aParent, aLeft, aTop, aWidth, aHeight);
|
||||
fCaption := aCaption;
|
||||
ShapeColor := aShapeColor;
|
||||
fFont := aFont;
|
||||
fFontHeight := gRes.Fonts[fFont].BaseHeight + 2;
|
||||
FontColor := icWhite;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMFlatButtonShape.Paint;
|
||||
begin
|
||||
inherited;
|
||||
|
||||
TKMRenderUI.WriteBevel(AbsLeft, AbsTop, Width, Height);
|
||||
|
||||
//Shape within bevel
|
||||
TKMRenderUI.WriteShape(AbsLeft + 1, AbsTop + 1, Width - 2, Width - 2, ShapeColor);
|
||||
|
||||
TKMRenderUI.WriteText(AbsLeft, AbsTop + (Height - fFontHeight) div 2,
|
||||
Width, fCaption, fFont, taCenter, FontColor);
|
||||
|
||||
if (csOver in State) and Enabled then
|
||||
TKMRenderUI.WriteShape(AbsLeft + 1, AbsTop + 1, Width - 2, Height - 2, $40FFFFFF);
|
||||
|
||||
if (csDown in State) or Down then
|
||||
TKMRenderUI.WriteOutline(AbsLeft, AbsTop, Width, Height, 1, icWhite);
|
||||
end;
|
||||
|
||||
|
||||
end.
|
||||
|
||||
+519
@@ -0,0 +1,519 @@
|
||||
unit KM_ControlsChart;
|
||||
{$I KaM_Remake.inc}
|
||||
interface
|
||||
uses
|
||||
Classes, Controls,
|
||||
KromOGLUtils,
|
||||
KM_Controls,
|
||||
KM_RenderUI, KM_ResFonts,
|
||||
KM_CommonClasses, KM_CommonTypes, KM_Points, KM_Defaults;
|
||||
|
||||
|
||||
type
|
||||
TKMNotifyLegendClick = procedure(Sender: TObject; aLineIndex: Integer; aLineVisible: Boolean) of object;
|
||||
|
||||
TKMGraphLine = record
|
||||
Title: UnicodeString;
|
||||
TitleDetailed: TKMStringArray;
|
||||
TitleDetailedColor: TKMCardinalArray;
|
||||
Tag: Integer;
|
||||
Color: TColor4;
|
||||
Visible: Boolean;
|
||||
Values: TKMCardinalArray;
|
||||
ValuesAlt: TKMCardinalArray;
|
||||
end;
|
||||
|
||||
TKMChart = class(TKMControl)
|
||||
private
|
||||
fCaption: UnicodeString;
|
||||
fFont: TKMFont;
|
||||
fCount: Integer;
|
||||
fItemHeight: Byte;
|
||||
fLegendWidth: Word;
|
||||
fLegendCaption: String;
|
||||
fLineOver: Integer;
|
||||
fLines: array of TKMGraphLine;
|
||||
fMaxLength: Cardinal; //Maximum samples (by horizontal axis)
|
||||
fMinTime: Cardinal; //Minimum time (in sec), used only for Rendering time ticks
|
||||
fMaxTime: Cardinal; //Maximum time (in sec), used only for Rendering time ticks
|
||||
fMaxValue: Cardinal; //Maximum value (by vertical axis)
|
||||
fPeaceTime: Cardinal;
|
||||
|
||||
//Legend separators
|
||||
fSeparatorPositions: TXStringList;
|
||||
fSeparatorHeight: Byte;
|
||||
fSeparatorColor: TColor4;
|
||||
|
||||
fOnLegendClick: TKMNotifyLegendClick;
|
||||
procedure UpdateMaxValue;
|
||||
function GetLine(aIndex:Integer): TKMGraphLine;
|
||||
function GetLineNumber(aY: Integer): Integer;
|
||||
// function GetSeparatorsHeight(aIndex: Integer): Integer;
|
||||
function GetSeparatorPos(aIndex: Integer): Integer;
|
||||
public
|
||||
constructor Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer);
|
||||
destructor Destroy; override;
|
||||
|
||||
procedure AddLine(const aTitle: UnicodeString; aColor: TColor4; const aValues: TKMCardinalArray; aTag: Integer = -1); overload;
|
||||
procedure AddLine(const aTitle: UnicodeString; aColor: TColor4; const aTitleDetailed: TKMStringArray;
|
||||
const aTitleDetailedColor: TKMCardinalArray; const aValues: TKMCardinalArray; aTag: Integer = -1); overload;
|
||||
procedure AddAltLine(const aAltValues: TKMCardinalArray);
|
||||
procedure TrimToFirstVariation;
|
||||
property Caption: UnicodeString read fCaption write fCaption;
|
||||
procedure Clear;
|
||||
procedure SetLineVisible(aLineID:Integer; aVisible:Boolean);
|
||||
property MaxLength: Cardinal read fMaxLength write fMaxLength;
|
||||
property MaxTime: Cardinal read fMaxTime write fMaxTime;
|
||||
property Lines[aIndex: Integer]: TKMGraphLine read GetLine;
|
||||
property LineCount:Integer read fCount;
|
||||
property Font: TKMFont read fFont write fFont;
|
||||
property LegendWidth: Word read fLegendWidth write fLegendWidth;
|
||||
property LegendCaption: String read fLegendCaption write fLegendCaption;
|
||||
property Peacetime: Cardinal read fPeaceTime write fPeaceTime;
|
||||
|
||||
property SeparatorPos[aIndex: Integer]: Integer read GetSeparatorPos;
|
||||
property SeparatorColor: TColor4 read fSeparatorColor write fSeparatorColor;
|
||||
property SeparatorHeight: Byte read fSeparatorHeight write fSeparatorHeight;
|
||||
|
||||
procedure AddSeparator(aPosition: Integer);
|
||||
procedure SetSeparatorPositions(aSeparatorPositions: TStringList);
|
||||
procedure ClearSeparators;
|
||||
|
||||
property OnLegendClick: TKMNotifyLegendClick read fOnLegendClick write fOnLegendClick;
|
||||
|
||||
procedure MouseMove(X,Y: Integer; Shift: TShiftState); override;
|
||||
procedure MouseUp(X,Y: Integer; Shift: TShiftState; Button: TMouseButton); override;
|
||||
|
||||
procedure Paint; override;
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
uses
|
||||
SysUtils, Math,
|
||||
KM_Resource, KM_ResTexts,
|
||||
KM_CommonUtils;
|
||||
|
||||
|
||||
{ TKMChart }
|
||||
constructor TKMChart.Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer);
|
||||
begin
|
||||
inherited Create(aParent, aLeft, aTop, aWidth, aHeight);
|
||||
|
||||
fSeparatorPositions := TXStringList.Create;
|
||||
fSeparatorPositions.Sorted := True; // Better we have separators sorted
|
||||
|
||||
fFont := fntOutline;
|
||||
fItemHeight := 20;
|
||||
fLineOver := -1;
|
||||
fLegendWidth := 150;
|
||||
fSeparatorColor := clChartSeparator;
|
||||
fSeparatorHeight := 10;
|
||||
end;
|
||||
|
||||
|
||||
destructor TKMChart.Destroy;
|
||||
begin
|
||||
FreeAndNil(fSeparatorPositions);
|
||||
inherited;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMChart.AddLine(const aTitle: UnicodeString; aColor: TColor4; const aValues: TKMCardinalArray; aTag: Integer = -1);
|
||||
var
|
||||
titleDetailed: TKMStringArray;
|
||||
titleDetailedColor: TKMCardinalArray;
|
||||
begin
|
||||
SetLength(titleDetailed, 0);
|
||||
SetLength(titleDetailedColor, 0);
|
||||
AddLine(aTitle, aColor, titleDetailed, titleDetailedColor, aValues, aTag);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMChart.AddLine(const aTitle: UnicodeString; aColor: TColor4; const aTitleDetailed: TKMStringArray;
|
||||
const aTitleDetailedColor: TKMCardinalArray; const aValues: TKMCardinalArray; aTag: Integer = -1);
|
||||
begin
|
||||
if fMaxLength = 0 then Exit;
|
||||
|
||||
Assert(Length(aTitleDetailed) = Length(aTitleDetailedColor), 'aTitleDetailed and aTitleDetailedColor should have same length');
|
||||
|
||||
//Make sure there is enough Values to copy to local storage with Move procedure
|
||||
Assert(Length(aValues) >= fMaxLength);
|
||||
|
||||
SetLength(fLines, fCount + 1);
|
||||
|
||||
fLines[fCount].Color := aColor;
|
||||
fLines[fCount].Title := aTitle;
|
||||
fLines[fCount].Tag := aTag;
|
||||
fLines[fCount].Visible := True;
|
||||
fLines[fCount].TitleDetailed := aTitleDetailed;
|
||||
fLines[fCount].TitleDetailedColor := aTitleDetailedColor;
|
||||
SetLength(fLines[fCount].Values, fMaxLength);
|
||||
if SizeOf(aValues) <> 0 then
|
||||
Move(aValues[0], fLines[fCount].Values[0], SizeOf(aValues[0]) * fMaxLength);
|
||||
Inc(fCount);
|
||||
|
||||
UpdateMaxValue;
|
||||
end;
|
||||
|
||||
|
||||
function TKMChart.GetSeparatorPos(aIndex: Integer): Integer;
|
||||
begin
|
||||
Result := -1;
|
||||
if not InRange(aIndex, 0, fSeparatorPositions.Count - 1) then Exit;
|
||||
|
||||
Result := StrToInt(fSeparatorPositions[aIndex]);
|
||||
end;
|
||||
|
||||
|
||||
//function TKMChart.GetSeparatorsHeight(aIndex: Integer): Integer;
|
||||
//var
|
||||
// I, Pos: Integer;
|
||||
//begin
|
||||
// Result := 0;
|
||||
// for I := 0 to fSeparatorPositions.Count - 1 do
|
||||
// begin
|
||||
// Pos := SeparatorPos[I];
|
||||
// if (Pos <> -1) and (Pos <= aIndex) then
|
||||
// Inc(Result, fSeparatorHeight);
|
||||
// end;
|
||||
//end;
|
||||
|
||||
|
||||
procedure TKMChart.AddSeparator(aPosition: Integer);
|
||||
begin
|
||||
fSeparatorPositions.Add(IntToStr(aPosition));
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMChart.SetSeparatorPositions(aSeparatorPositions: TStringList);
|
||||
begin
|
||||
fSeparatorPositions.Clear;
|
||||
fSeparatorPositions.AddStrings(aSeparatorPositions);
|
||||
end;
|
||||
|
||||
|
||||
//Add alternative values line (e.g. wares count vs. wares produced)
|
||||
procedure TKMChart.AddAltLine(const aAltValues: TKMCardinalArray);
|
||||
begin
|
||||
Assert(Length(aAltValues) >= fMaxLength);
|
||||
|
||||
SetLength(fLines[fCount-1].ValuesAlt, fMaxLength);
|
||||
if SizeOf(aAltValues) <> 0 then
|
||||
Move(aAltValues[0], fLines[fCount-1].ValuesAlt[0], SizeOf(aAltValues[0]) * fMaxLength);
|
||||
|
||||
UpdateMaxValue;
|
||||
end;
|
||||
|
||||
|
||||
//Trims the graph until 5% before the first variation
|
||||
procedure TKMChart.TrimToFirstVariation;
|
||||
var
|
||||
I, K, firstVarSample: Integer;
|
||||
startVal: Cardinal;
|
||||
begin
|
||||
firstVarSample := -1;
|
||||
for I:=0 to fCount-1 do
|
||||
if Length(fLines[I].Values) > 0 then
|
||||
begin
|
||||
startVal := fLines[I].Values[0];
|
||||
for K:=1 to Length(fLines[I].Values)-1 do
|
||||
if fLines[I].Values[K] <> startVal then
|
||||
begin
|
||||
if (K < firstVarSample) or (firstVarSample = -1) then
|
||||
firstVarSample := K - 1;
|
||||
Break;
|
||||
end;
|
||||
end;
|
||||
if firstVarSample <= 0 then
|
||||
begin
|
||||
fMinTime := 0; //No variation at all, so don't trim it (but clear previous value)
|
||||
Exit;
|
||||
end;
|
||||
//Take 5% before the first varied sample
|
||||
firstVarSample := Max(0, firstVarSample - Max(1, Round(0.05*(fMaxLength - firstVarSample))));
|
||||
//Trim all fLines[I].Values to start at FirstVarSample
|
||||
for I := 0 to fCount - 1 do
|
||||
begin
|
||||
Move(fLines[I].Values[firstVarSample], fLines[I].Values[0], (Length(fLines[I].Values)-firstVarSample)*SizeOf(fLines[I].Values[0]));
|
||||
SetLength(fLines[I].Values, Length(fLines[I].Values)-firstVarSample);
|
||||
end;
|
||||
//Set start time so the horizontal time ticks are rendered correctly
|
||||
fMinTime := Round((firstVarSample/fMaxLength) * fMaxTime);
|
||||
//All lines have now been trimmed, so update fMaxLength
|
||||
fMaxLength := fMaxLength - firstVarSample;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMChart.Clear;
|
||||
begin
|
||||
fCount := 0;
|
||||
SetLength(fLines, 0);
|
||||
fMaxValue := 0;
|
||||
ClearSeparators;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMChart.ClearSeparators;
|
||||
begin
|
||||
fSeparatorPositions.Clear;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMChart.SetLineVisible(aLineID: Integer; aVisible: Boolean);
|
||||
begin
|
||||
fLines[aLineID].Visible := aVisible;
|
||||
UpdateMaxValue;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMChart.UpdateMaxValue;
|
||||
var
|
||||
I, K: Integer;
|
||||
begin
|
||||
fMaxValue := 0;
|
||||
for I := 0 to fCount - 1 do
|
||||
if fLines[I].Visible then
|
||||
for K := 0 to fMaxLength - 1 do
|
||||
if fLines[I].Values[K] > fMaxValue then
|
||||
fMaxValue := fLines[I].Values[K];
|
||||
end;
|
||||
|
||||
|
||||
function TKMChart.GetLine(aIndex: Integer): TKMGraphLine;
|
||||
begin
|
||||
Result := fLines[aIndex];
|
||||
end;
|
||||
|
||||
|
||||
function TKMChart.GetLineNumber(aY: Integer): Integer;
|
||||
var
|
||||
I, S, lineTop, lineBottom: Integer;
|
||||
begin
|
||||
Result := -1;
|
||||
S := 0;
|
||||
lineTop := AbsTop + 5 + 20*Byte(fLegendCaption <> '');
|
||||
for I := 0 to fCount - 1 do
|
||||
begin
|
||||
if SeparatorPos[S] = I then
|
||||
begin
|
||||
Inc(lineTop, fSeparatorHeight);
|
||||
Inc(S);
|
||||
end;
|
||||
lineBottom := lineTop + fItemHeight*(1 + Length(Lines[I].TitleDetailed));
|
||||
if InRange(aY, lineTop, lineBottom) then
|
||||
begin
|
||||
Result := I;
|
||||
Exit;
|
||||
end;
|
||||
lineTop := lineBottom;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMChart.MouseMove(X, Y: Integer; Shift: TShiftState);
|
||||
begin
|
||||
inherited;
|
||||
|
||||
fLineOver := -1;
|
||||
if X < AbsLeft + Width - fLegendWidth + 5 then Exit;
|
||||
fLineOver := GetLineNumber(Y);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMChart.MouseUp(X, Y: Integer; Shift: TShiftState; Button: TMouseButton);
|
||||
var
|
||||
I: Integer;
|
||||
begin
|
||||
inherited;
|
||||
|
||||
if X < AbsLeft + Width - fLegendWidth+5 then Exit;
|
||||
|
||||
I := GetLineNumber(Y);
|
||||
if not InRange(I, 0, fCount - 1) then Exit;
|
||||
|
||||
fLines[I].Visible := not fLines[I].Visible;
|
||||
|
||||
UpdateMaxValue;
|
||||
|
||||
if Assigned(fOnLegendClick) then
|
||||
fOnLegendClick(Self, I, fLines[I].Visible);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMChart.Paint;
|
||||
const
|
||||
INTERVAL_CNT: array [0..9] of Word = (1, 5, 10, 50, 100, 500, 1000, 5000, 10000, 50000);
|
||||
INTERVAL_TIME: array [0..10] of Word = (30, 1*60, 5*60, 10*60, 15*60, 30*60, 1*60*60, 2*60*60, 3*60*60, 4*60*60, 5*60*60);
|
||||
|
||||
var
|
||||
G: TKMRect;
|
||||
topValue: Integer;
|
||||
|
||||
procedure PaintAxisLabel(aTime: Integer; aIsPT: Boolean = False);
|
||||
var
|
||||
xPos: Integer;
|
||||
begin
|
||||
xPos := G.Left + Round((aTime - fMinTime) / (fMaxTime-fMinTime) * (G.Right - G.Left));
|
||||
TKMRenderUI.WriteShape(xPos, G.Bottom - 2, 2, 5, IfThen(aIsPT, clChartPeacetimeLn, icWhite));
|
||||
TKMRenderUI.WriteText (xPos, G.Bottom + 4, 0, TimeToString(aTime / 24 / 60 / 60), fntGame, taLeft, IfThen(aIsPT, clChartPeacetimeLbl, icWhite));
|
||||
TKMRenderUI.WriteLine(xPos, G.Top, xPos, G.Bottom, IfThen(aIsPT, clChartPeacetimeLn, clChartDashedVLn), $CCCC);
|
||||
if aIsPT then
|
||||
TKMRenderUI.WriteText(xPos - 3, G.Bottom + 4, 0, gResTexts[TX_CHART_PT_END], fntGame, taRight, clChartPeacetimeLbl);
|
||||
end;
|
||||
|
||||
procedure RenderHorizontalAxisTicks;
|
||||
var
|
||||
I, best: Integer;
|
||||
begin
|
||||
//Find first time interval that will have less than 10 ticks
|
||||
best := 0;
|
||||
for I := Low(INTERVAL_TIME) to High(INTERVAL_TIME) do
|
||||
if (fMaxTime-fMinTime) div INTERVAL_TIME[I] < 7 then
|
||||
begin
|
||||
best := INTERVAL_TIME[I];
|
||||
Break;
|
||||
end;
|
||||
|
||||
//Paint time axis labels
|
||||
if (best <> 0) and (fMaxTime <> fMinTime) then
|
||||
if (fPeaceTime <> 0) and InRange(fPeaceTime, fMinTime, fMaxTime) then
|
||||
begin
|
||||
//Labels before PT and PT himself
|
||||
for I := 0 to ((fPeaceTime - fMinTime) div best) do
|
||||
PaintAxisLabel(fPeaceTime - I * best, I = 0);
|
||||
|
||||
//Labels after PT
|
||||
for I := 1 to ((fMaxTime - fPeaceTime) div best) do
|
||||
PaintAxisLabel(fPeaceTime + I * best);
|
||||
end else
|
||||
for I := Ceil(fMinTime / best) to (fMaxTime div best) do
|
||||
PaintAxisLabel(I * best);
|
||||
end;
|
||||
|
||||
function GetLineColor(aColor: Cardinal): Cardinal;
|
||||
begin
|
||||
//Adjust the color if it blends with black background
|
||||
Result := EnsureBrightness(aColor, 0.3);
|
||||
|
||||
// If color is similar to highlight color, then use alternative HL color
|
||||
if GetColorDistance(Result, clChartHighlight) < 0.1 then
|
||||
Result := clChartHighlight2;
|
||||
end;
|
||||
|
||||
procedure RenderChartAndLegend;
|
||||
const
|
||||
MARKS_FONT: TKMFont = fntGrey;
|
||||
var
|
||||
I, J, S, checkSize, xPos, yPos, height: Integer;
|
||||
titleDetailedH: Integer;
|
||||
newColor: TColor4;
|
||||
begin
|
||||
checkSize := gRes.Fonts[MARKS_FONT].GetTextSize('v').Y + 1;
|
||||
S := 0;
|
||||
xPos := G.Right + 10;
|
||||
yPos := G.Top + 8 + 20*Byte(fLegendCaption <> '');
|
||||
|
||||
titleDetailedH := 0;
|
||||
//Charts and legend
|
||||
for I := 0 to fCount - 1 do
|
||||
begin
|
||||
newColor := GetLineColor(fLines[I].Color);
|
||||
|
||||
if (csOver in State) and (I = fLineOver) then
|
||||
newColor := clChartHighlight;
|
||||
|
||||
//Charts
|
||||
if fLines[I].Visible then
|
||||
begin
|
||||
TKMRenderUI.WritePlot(G.Left, G.Top, G.Right-G.Left, G.Bottom-G.Top, fLines[I].Values, topValue, newColor, 2);
|
||||
if Length(fLines[I].ValuesAlt) > 0 then
|
||||
TKMRenderUI.WritePlot(G.Left, G.Top, G.Right-G.Left, G.Bottom-G.Top, fLines[I].ValuesAlt, topValue, newColor, 1);
|
||||
end;
|
||||
|
||||
if SeparatorPos[S] = I then
|
||||
begin
|
||||
Inc(yPos, fSeparatorHeight);
|
||||
Inc(S);
|
||||
end;
|
||||
|
||||
//Checkboxes
|
||||
TKMRenderUI.WriteBevel(xPos, yPos, checkSize - 4, checkSize - 4, 1, 0.3);
|
||||
TKMRenderUI.WriteOutline(xPos, yPos, checkSize - 4, checkSize - 4, 1, clChkboxOutline);
|
||||
if fLines[I].Visible then
|
||||
TKMRenderUI.WriteText(xPos + (checkSize-4) div 2, yPos - 1, 0, 'v', MARKS_FONT, taCenter, newColor);
|
||||
|
||||
//Legend
|
||||
TKMRenderUI.WriteText(xPos + checkSize, yPos, 0, fLines[I].Title, fntGame, taLeft, newColor);
|
||||
Inc(yPos, fItemHeight);
|
||||
|
||||
//Detailed legend
|
||||
for J := Low(fLines[I].TitleDetailed) to High(fLines[I].TitleDetailed) do
|
||||
begin
|
||||
TKMRenderUI.WriteText(xPos + checkSize + 5, yPos, 0, fLines[I].TitleDetailed[J], fntGrey, taLeft, GetLineColor(fLines[I].TitleDetailedColor[J]));
|
||||
Inc(yPos, fItemHeight);
|
||||
Inc(titleDetailedH, fItemHeight);
|
||||
end;
|
||||
end;
|
||||
|
||||
//Legend title and outline
|
||||
height := fItemHeight*fCount + titleDetailedH + 6 + 20*Byte(fLegendCaption <> '') + fSeparatorPositions.Count*fSeparatorHeight;
|
||||
TKMRenderUI.WriteShape(G.Right + 5, G.Top, fLegendWidth, height, icDarkestGrayTrans);
|
||||
TKMRenderUI.WriteOutline(G.Right + 5, G.Top, fLegendWidth, height, 1, icGray);
|
||||
if fLegendCaption <> '' then
|
||||
TKMRenderUI.WriteText(G.Right + 5, G.Top + 4, fLegendWidth, fLegendCaption, fntMetal, taCenter, icWhite);
|
||||
end;
|
||||
|
||||
var
|
||||
I: Integer;
|
||||
best, tmp: Integer;
|
||||
begin
|
||||
inherited;
|
||||
|
||||
G := KMRect(AbsLeft + 40, AbsTop, AbsLeft + Width - fLegendWidth, AbsTop + Height - 20);
|
||||
|
||||
//Add margin to MaxValue so that it does not blends with upper border
|
||||
topValue := Max(Round(fMaxValue * 1.1), fMaxValue + 1);
|
||||
|
||||
//Find first interval that will have less than 10 ticks
|
||||
best := 0;
|
||||
for I := Low(INTERVAL_CNT) to High(INTERVAL_CNT) do
|
||||
if topValue div INTERVAL_CNT[I] < 10 then
|
||||
begin
|
||||
best := INTERVAL_CNT[I];
|
||||
Break;
|
||||
end;
|
||||
|
||||
//Dashed lines in the background
|
||||
if best <> 0 then
|
||||
for I := 1 to (topValue div best) do
|
||||
begin
|
||||
tmp := G.Top + Round((1 - I * best / topValue) * (G.Bottom - G.Top));
|
||||
TKMRenderUI.WriteText(G.Left - 5, tmp - 6, 0, IntToStr(I * best), fntGame, taRight);
|
||||
TKMRenderUI.WriteLine(G.Left, tmp, G.Right, tmp, clChartDashedHLn, $CCCC);
|
||||
end;
|
||||
|
||||
//Render horizontal axis ticks
|
||||
RenderHorizontalAxisTicks;
|
||||
|
||||
RenderChartAndLegend;
|
||||
|
||||
//Render the highlighted line above all the others and thicker so you can see where it goes under others
|
||||
if (csOver in State) and InRange(fLineOver, 0, fCount-1) and fLines[fLineOver].Visible then
|
||||
TKMRenderUI.WritePlot(G.Left, G.Top, G.Right-G.Left, G.Bottom-G.Top, fLines[fLineOver].Values, topValue, clChartHighlight, 3);
|
||||
|
||||
//Outline
|
||||
TKMRenderUI.WriteOutline(G.Left, G.Top, G.Right-G.Left, G.Bottom-G.Top, 1, icWhite);
|
||||
|
||||
//Title
|
||||
TKMRenderUI.WriteText(G.Left + 5, G.Top + 5, 0, fCaption, fFont, taLeft);
|
||||
|
||||
//Render vertical axis captions
|
||||
TKMRenderUI.WriteText(G.Left - 5, G.Bottom - 6, 0, IntToStr(0), fntGame, taRight);
|
||||
//TKMRenderUI.WriteText(Left+20, Top + 20, 0, 0, IntToStr(fMaxValue), fntGame, taRight);
|
||||
|
||||
end;
|
||||
|
||||
|
||||
end.
|
||||
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
unit KM_ControlsColor;
|
||||
{$I KaM_Remake.inc}
|
||||
interface
|
||||
uses
|
||||
Classes, Controls,
|
||||
KromOGLUtils,
|
||||
KM_Controls, KM_ControlsBase,
|
||||
KM_CommonTypes;
|
||||
|
||||
|
||||
type
|
||||
// Color swatch - to select a color from given samples/palette
|
||||
TKMColorSwatch = class(TKMControl)
|
||||
private
|
||||
fBackAlpha: single; // Alpha of background (usually 0.5, dropbox 1)
|
||||
fCellSize: Byte; // Size of the square in pixels
|
||||
fColumnCount: Byte;
|
||||
fRowCount: Byte;
|
||||
fColorIndex: Integer;
|
||||
Colors: array of TColor4;
|
||||
fOnChange: TNotifyEvent;
|
||||
fInclRandom: Boolean;
|
||||
public
|
||||
constructor Create(aParent: TKMPanel; aLeft,aTop,aColumnCount,aRowCount,aSize: Integer);
|
||||
procedure SetColors(const aColors: array of TColor4; aInclRandom: Boolean = False);
|
||||
procedure SelectByColor(aColor: TColor4);
|
||||
property BackAlpha: single read fBackAlpha write fBackAlpha;
|
||||
property ColorIndex: Integer read fColorIndex write fColorIndex;
|
||||
function GetColor: TColor4;
|
||||
procedure MouseUp(X,Y: Integer; Shift: TShiftState; Button: TMouseButton); override;
|
||||
property OnChange: TNotifyEvent write fOnChange;
|
||||
procedure Paint; override;
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
uses
|
||||
Math,
|
||||
KM_RenderUI,
|
||||
KM_ResFonts, KM_ResTypes,
|
||||
KM_CommonUtils;
|
||||
|
||||
|
||||
{ TKMColorSwatch }
|
||||
constructor TKMColorSwatch.Create(aParent: TKMPanel; aLeft,aTop,aColumnCount,aRowCount,aSize: Integer);
|
||||
begin
|
||||
inherited Create(aParent, aLeft, aTop, 0, 0);
|
||||
|
||||
fBackAlpha := 0.5;
|
||||
fColumnCount := aColumnCount;
|
||||
fRowCount := aRowCount;
|
||||
fCellSize := aSize;
|
||||
fInclRandom := False;
|
||||
fColorIndex := -1;
|
||||
|
||||
Width := fColumnCount * fCellSize;
|
||||
Height := fRowCount * fCellSize;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMColorSwatch.SetColors(const aColors: array of TColor4; aInclRandom: Boolean = False);
|
||||
begin
|
||||
fInclRandom := aInclRandom;
|
||||
if fInclRandom then
|
||||
begin
|
||||
SetLength(Colors, Length(aColors)+SizeOf(TColor4));
|
||||
Colors[0] := $00000000; //This one is reserved for random
|
||||
Move((@aColors[0])^, (@Colors[1])^, SizeOf(aColors));
|
||||
end
|
||||
else
|
||||
begin
|
||||
SetLength(Colors, Length(aColors));
|
||||
Move((@aColors[0])^, (@Colors[0])^, SizeOf(aColors));
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMColorSwatch.SelectByColor(aColor: TColor4);
|
||||
var
|
||||
I: Integer;
|
||||
begin
|
||||
fColorIndex := -1;
|
||||
for I:=0 to Length(Colors)-1 do
|
||||
if Colors[I] = aColor then
|
||||
fColorIndex := I;
|
||||
end;
|
||||
|
||||
|
||||
function TKMColorSwatch.GetColor: TColor4;
|
||||
begin
|
||||
if fColorIndex <> -1 then
|
||||
Result := Colors[fColorIndex]
|
||||
else
|
||||
Result := $FF000000; //Black by default
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMColorSwatch.MouseUp(X,Y: Integer; Shift: TShiftState; Button: TMouseButton);
|
||||
var
|
||||
newColor: Integer;
|
||||
begin
|
||||
if Button = mbLeft then
|
||||
begin
|
||||
newColor := EnsureRange((Y-AbsTop) div fCellSize, 0, fRowCount-1)*fColumnCount +
|
||||
EnsureRange((X-AbsLeft) div fCellSize, 0, fColumnCount-1);
|
||||
if InRange(newColor, 0, Length(Colors)-1) then
|
||||
begin
|
||||
fColorIndex := newColor;
|
||||
if Assigned(fOnChange) then fOnChange(Self);
|
||||
end;
|
||||
end;
|
||||
|
||||
inherited;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMColorSwatch.Paint;
|
||||
var
|
||||
I, start: Integer;
|
||||
selColor: TColor4;
|
||||
begin
|
||||
inherited;
|
||||
|
||||
TKMRenderUI.WriteBevel(AbsLeft, AbsTop, Width, Height, 1, fBackAlpha);
|
||||
|
||||
start := 0;
|
||||
if fInclRandom then
|
||||
begin
|
||||
//Render miniature copy of all available colors with '?' on top
|
||||
for I := 0 to Length(Colors) - 1 do
|
||||
TKMRenderUI.WriteShape(AbsLeft+(I mod fColumnCount)*(fCellSize div fColumnCount)+2, AbsTop+(I div fColumnCount)*(fCellSize div fColumnCount)+2, (fCellSize div fColumnCount), (fCellSize div fColumnCount), Colors[I]);
|
||||
TKMRenderUI.WriteText(AbsLeft + fCellSize div 2, AbsTop + fCellSize div 4, 0, '?', fntMetal, taCenter);
|
||||
start := 1;
|
||||
end;
|
||||
|
||||
for I := start to Length(Colors) - 1 do
|
||||
TKMRenderUI.WriteShape(AbsLeft+(I mod fColumnCount)*fCellSize, AbsTop+(I div fColumnCount)*fCellSize, fCellSize, fCellSize, Colors[I]);
|
||||
|
||||
if fColorIndex < 0 then Exit;
|
||||
|
||||
if GetColorBrightness(Colors[fColorIndex]) >= 0.5 then
|
||||
selColor := $FF000000
|
||||
else
|
||||
selColor := $FFFFFFFF;
|
||||
|
||||
//Paint selection
|
||||
TKMRenderUI.WriteOutline(AbsLeft+(fColorIndex mod fColumnCount)*fCellSize, AbsTop+(fColorIndex div fColumnCount)*fCellSize, fCellSize, fCellSize, 1, selColor);
|
||||
end;
|
||||
|
||||
|
||||
end.
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
unit KM_ControlsDragger;
|
||||
{$I KaM_Remake.inc}
|
||||
interface
|
||||
uses
|
||||
Classes, Controls,
|
||||
KM_Controls;
|
||||
|
||||
|
||||
type
|
||||
//Element that player can drag within allowed bounds
|
||||
TKMDragger = class(TKMControl)
|
||||
private
|
||||
fMinusX, fMinusY, fPlusX, fPlusY: Integer; //Restrictions
|
||||
fPositionX: Integer;
|
||||
fPositionY: Integer;
|
||||
fStartDragX: Integer;
|
||||
fStartDragY: Integer;
|
||||
public
|
||||
OnMove: TKMNotifyEventXY;
|
||||
constructor Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer);
|
||||
|
||||
procedure SetBounds(aMinusX, aMinusY, aPlusX, aPlusY: Integer);
|
||||
|
||||
procedure MouseDown(X,Y: Integer; Shift: TShiftState; Button: TMouseButton); override;
|
||||
procedure MouseMove(X,Y: Integer; Shift: TShiftState); override;
|
||||
procedure MouseUp(X,Y: Integer; Shift: TShiftState; Button: TMouseButton); override;
|
||||
|
||||
procedure Paint; override;
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
uses
|
||||
Math,
|
||||
KM_RenderUI,
|
||||
KM_ResTypes;
|
||||
|
||||
|
||||
{ TKMDragger }
|
||||
constructor TKMDragger.Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer);
|
||||
begin
|
||||
inherited Create(aParent, aLeft, aTop, aWidth, aHeight);
|
||||
|
||||
//Original position is used to resrict movement
|
||||
fPositionX := 0;
|
||||
fPositionY := 0;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDragger.SetBounds(aMinusX, aMinusY, aPlusX, aPlusY: Integer);
|
||||
begin
|
||||
fMinusX := aMinusX;
|
||||
fMinusY := aMinusY;
|
||||
fPlusX := aPlusX;
|
||||
fPlusY := aPlusY;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDragger.MouseDown(X,Y: Integer; Shift: TShiftState; Button: TMouseButton);
|
||||
begin
|
||||
inherited;
|
||||
fStartDragX := X - fPositionX;
|
||||
fStartDragY := Y - fPositionY;
|
||||
|
||||
MouseMove(X,Y,Shift);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDragger.MouseMove(X,Y: Integer; Shift: TShiftState);
|
||||
begin
|
||||
inherited;
|
||||
|
||||
if csDown in State then
|
||||
begin
|
||||
//Bounds are signed numbers, set them properly
|
||||
fPositionX := EnsureRange((X - fStartDragX), fMinusX, fPlusX);
|
||||
fPositionY := EnsureRange((Y - fStartDragY), fMinusY, fPlusY);
|
||||
|
||||
if Assigned(OnMove) then OnMove(Self, fPositionX, fPositionY);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDragger.MouseUp(X,Y: Integer; Shift: TShiftState; Button: TMouseButton);
|
||||
begin
|
||||
inherited;
|
||||
MouseMove(X,Y,Shift);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDragger.Paint;
|
||||
var
|
||||
stateSet: TKMButtonStateSet;
|
||||
begin
|
||||
inherited;
|
||||
stateSet := [];
|
||||
if (csOver in State) and Enabled then
|
||||
stateSet := stateSet + [bsOver];
|
||||
if (csDown in State) then
|
||||
stateSet := stateSet + [bsDown];
|
||||
if not Enabled then
|
||||
stateSet := stateSet + [bsDisabled];
|
||||
|
||||
TKMRenderUI.Write3DButton(AbsLeft, AbsTop, Width, Height, rxGui, 0, $FFFF00FF, stateSet, bsGame);
|
||||
end;
|
||||
|
||||
|
||||
end.
|
||||
|
||||
+792
@@ -0,0 +1,792 @@
|
||||
unit KM_ControlsDrop;
|
||||
interface
|
||||
uses
|
||||
Classes,
|
||||
Controls,
|
||||
KromOGLUtils,
|
||||
KM_Controls, KM_ControlsBase, KM_ControlsList,
|
||||
KM_ResTypes, KM_ResFonts,
|
||||
KM_RenderUI;
|
||||
|
||||
|
||||
type
|
||||
TKMDropCommon = class(TKMControl)
|
||||
private
|
||||
fDropCount: Integer;
|
||||
fDropUp: Boolean;
|
||||
fFont: TKMFont;
|
||||
fButton: TKMButton;
|
||||
fShape: TKMShape;
|
||||
fAutoClose: Boolean;
|
||||
|
||||
fOnChange: TNotifyEvent;
|
||||
fOnShowList: TNotifyEvent;
|
||||
|
||||
procedure UpdateDropPosition; virtual; abstract;
|
||||
procedure ButtonClick(Sender: TObject);
|
||||
procedure ListShow(Sender: TObject); virtual;
|
||||
procedure ListClick(Sender: TObject); virtual;
|
||||
procedure ListChange(Sender: TObject); virtual;
|
||||
procedure ListHide(Sender: TObject); virtual;
|
||||
function ListVisible: Boolean; virtual; abstract;
|
||||
function GetItemIndex: SmallInt; virtual; abstract;
|
||||
procedure SetItemIndex(aIndex: SmallInt); virtual; abstract;
|
||||
protected
|
||||
procedure DoClick(X,Y: Integer; Shift: TShiftState; Button: TMouseButton); override;
|
||||
procedure SetLeft(aValue: Integer); override;
|
||||
procedure SetTop(aValue: Integer); override;
|
||||
procedure SetEnabled(aValue: Boolean); override;
|
||||
procedure SetVisible(aValue: Boolean); override;
|
||||
function ListKeyDown(Sender: TObject; Key: Word; Shift: TShiftState): Boolean;
|
||||
procedure UpdateVisibility; override;
|
||||
function GetCount: Integer; virtual; abstract;
|
||||
procedure SetDropCount(const aValue: Integer); virtual;
|
||||
public
|
||||
constructor Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; aFont: TKMFont; aStyle: TKMButtonStyle;
|
||||
aAutoClose: Boolean = True);
|
||||
|
||||
procedure Clear; virtual; abstract;
|
||||
procedure OpenList;
|
||||
procedure CloseList;
|
||||
procedure SetOpenList(aOpen: Boolean);
|
||||
procedure SwitchOpen;
|
||||
|
||||
property DropCount: Integer read fDropCount write SetDropCount;
|
||||
property DropUp: Boolean read fDropUp write fDropUp;
|
||||
property ItemIndex: SmallInt read GetItemIndex write SetItemIndex;
|
||||
function IsOpen: Boolean; virtual;
|
||||
property Count: Integer read GetCount;
|
||||
|
||||
property OnShowList: TNotifyEvent read fOnShowList write fOnShowList;
|
||||
property OnChange: TNotifyEvent read fOnChange write fOnChange;
|
||||
procedure Paint; override;
|
||||
end;
|
||||
|
||||
|
||||
//DropBox with a ListBox
|
||||
TKMDropList = class(TKMDropCommon)
|
||||
private
|
||||
fCaption: UnicodeString; //Current caption (Default or from list)
|
||||
fDefaultCaption: UnicodeString;
|
||||
fDropWidth: Integer;
|
||||
fList: TKMListBox;
|
||||
fListTopIndex: Integer;
|
||||
procedure UpdateDropPosition; override;
|
||||
procedure ListShow(Sender: TObject); override;
|
||||
procedure ListClick(Sender: TObject); override;
|
||||
procedure ListChange(Sender: TObject); override;
|
||||
procedure ListHide(Sender: TObject); override;
|
||||
function ListVisible: Boolean; override;
|
||||
function GetItem(aIndex: Integer): UnicodeString;
|
||||
function GetItemIndex: smallint; override;
|
||||
procedure SetItemIndex(aIndex: smallint); override;
|
||||
procedure SetDropWidth(aDropWidth: Integer);
|
||||
function GetShowHintWhenShort: Boolean;
|
||||
procedure SetShowHintWhenShort(const aValue: Boolean);
|
||||
protected
|
||||
procedure SetEnabled(aValue: Boolean); override;
|
||||
procedure SetVisible(aValue: Boolean); override;
|
||||
function GetCount: Integer; override;
|
||||
procedure SetDropCount(const aValue: Integer); override;
|
||||
public
|
||||
constructor Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; aFont: TKMFont; const aDefaultCaption: UnicodeString;
|
||||
aStyle: TKMButtonStyle; aAutoClose: Boolean = True; aBackAlpha: Single = 0.85);
|
||||
procedure Clear; override;
|
||||
procedure Add(const aItem: UnicodeString; aTag: Integer = 0);
|
||||
procedure SelectByName(const aText: UnicodeString);
|
||||
procedure SelectByTag(aTag: Integer);
|
||||
function HasTag(aTag: Integer): Boolean;
|
||||
function GetTag(aIndex: Integer): Integer;
|
||||
function GetSelectedTag: Integer;
|
||||
function IsSelected: Boolean;
|
||||
property ShowHintWhenShort: Boolean read GetShowHintWhenShort write SetShowHintWhenShort;
|
||||
property DefaultCaption: UnicodeString read fDefaultCaption write fDefaultCaption;
|
||||
property Item[aIndex: Integer]: UnicodeString read GetItem;
|
||||
property List: TKMListBox read fList;
|
||||
function IsOpen: Boolean; override;
|
||||
property DropWidth: Integer read fDropWidth write SetDropWidth;
|
||||
|
||||
procedure Paint; override;
|
||||
end;
|
||||
|
||||
|
||||
//DropBox with a ColumnBox
|
||||
TKMDropColumns = class(TKMDropCommon)
|
||||
private
|
||||
fDefaultCaption: UnicodeString;
|
||||
fDropWidth: Integer;
|
||||
fList: TKMColumnBox;
|
||||
fListTopIndex: Integer;
|
||||
fColumnsToShowWhenListHidden: array of Boolean; //which columns to show, when list is hidden
|
||||
procedure UpdateDropPosition; override;
|
||||
procedure ListShow(Sender: TObject); override;
|
||||
procedure ListClick(Sender: TObject); override;
|
||||
procedure ListChange(Sender: TObject); override;
|
||||
procedure ListHide(Sender: TObject); override;
|
||||
function ListVisible: Boolean; override;
|
||||
function GetItem(aIndex: Integer): TKMListRow;
|
||||
function GetItemIndex: Smallint; override;
|
||||
procedure SetItemIndex(aIndex: Smallint); override;
|
||||
procedure SetDropWidth(aDropWidth: Integer);
|
||||
function GetShowHintWhenShort: Boolean;
|
||||
procedure SetShowHintWhenShort(const aValue: Boolean);
|
||||
protected
|
||||
procedure SetEnabled(aValue: Boolean); override;
|
||||
procedure SetVisible(aValue: Boolean); override;
|
||||
function GetCount: Integer; override;
|
||||
public
|
||||
FadeImageWhenDisabled: Boolean;
|
||||
constructor Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; aFont: TKMFont; const aDefaultCaption: UnicodeString; aStyle: TKMButtonStyle; aShowHeader: Boolean = True);
|
||||
procedure Add(aItem: TKMListRow);
|
||||
procedure Clear; override;
|
||||
property List: TKMColumnBox read fList;
|
||||
property Item[aIndex: Integer]: TKMListRow read GetItem; default;
|
||||
procedure SetColumns(aFont: TKMFont; aColumns: array of string; aColumnOffsets: array of Word); overload;
|
||||
procedure SetColumns(aFont: TKMFont; aColumns: array of string; aColumnOffsets: array of Word; aColumnsToShowWhenListHidden: array of Boolean); overload;
|
||||
property DefaultCaption: UnicodeString read fDefaultCaption write fDefaultCaption;
|
||||
property DropWidth: Integer read fDropWidth write SetDropWidth;
|
||||
property ShowHintWhenShort: Boolean read GetShowHintWhenShort write SetShowHintWhenShort;
|
||||
|
||||
procedure Paint; override;
|
||||
end;
|
||||
|
||||
|
||||
|
||||
implementation
|
||||
uses
|
||||
{$IFDEF MSWindows} Windows, {$ENDIF}
|
||||
{$IFDEF Unix} LCLIntf, LCLType, {$ENDIF}
|
||||
Math,
|
||||
KM_Defaults;
|
||||
|
||||
|
||||
{ TKMDropCommon }
|
||||
constructor TKMDropCommon.Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; aFont: TKMFont;
|
||||
aStyle: TKMButtonStyle; aAutoClose: Boolean = True);
|
||||
var
|
||||
P: TKMPanel;
|
||||
begin
|
||||
inherited Create(aParent, aLeft, aTop, aWidth, aHeight);
|
||||
|
||||
fDropCount := 10;
|
||||
fDropUp := False;
|
||||
fFont := aFont;
|
||||
|
||||
fButton := TKMButton.Create(aParent, aLeft+aWidth-aHeight, aTop, aHeight, aHeight, 590, rxGui, aStyle);
|
||||
fButton.OnClick := ButtonClick;
|
||||
fButton.MakesSound := False;
|
||||
|
||||
P := MasterParent;
|
||||
fShape := TKMShape.Create(P, 0, 0, P.Width, P.Height);
|
||||
fShape.AnchorsStretch;
|
||||
fShape.OnClick := ListHide;
|
||||
|
||||
fAutoClose := aAutoClose;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropCommon.UpdateVisibility;
|
||||
begin
|
||||
inherited;
|
||||
if not Visible then
|
||||
CloseList;
|
||||
end;
|
||||
|
||||
|
||||
function TKMDropCommon.IsOpen: Boolean;
|
||||
begin
|
||||
Result := fShape.Visible;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropCommon.ButtonClick(Sender: TObject);
|
||||
begin
|
||||
//Call the DoDlick event to show the list AND generate DropBox.OnClick event
|
||||
DoClick(fButton.AbsLeft + fButton.Width div 2, fButton.AbsTop + fButton.Height div 2, [], mbLeft);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropCommon.ListShow(Sender: TObject);
|
||||
begin
|
||||
if ListVisible then
|
||||
begin
|
||||
ListHide(nil);
|
||||
Exit;
|
||||
end;
|
||||
|
||||
if fAutoClose and (Count > 0) then
|
||||
fShape.Show;
|
||||
|
||||
if Assigned(fOnShowList) then fOnShowList(Self);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropCommon.DoClick(X, Y: Integer; Shift: TShiftState; Button: TMouseButton);
|
||||
begin
|
||||
//do not invoke inherited here, to fully override parent DoClick method
|
||||
|
||||
//It's common behavior when click on dropbox will show the list
|
||||
if fAutoClose then
|
||||
ListShow(Self)
|
||||
else
|
||||
if not ListVisible then
|
||||
begin
|
||||
fButton.TexId := 591;
|
||||
ListShow(Self)
|
||||
end else begin
|
||||
fButton.TexId := 590;
|
||||
ListHide(Self);
|
||||
end;
|
||||
|
||||
inherited;
|
||||
end;
|
||||
|
||||
|
||||
//Handle KeyDown on List
|
||||
function TKMDropCommon.ListKeyDown(Sender: TObject; Key: Word; Shift: TShiftState): Boolean;
|
||||
begin
|
||||
if fAutoClose and (Key = VK_ESCAPE) then // Close List on ESC, if autoclosable
|
||||
begin
|
||||
ListHide(nil);
|
||||
Result := True;
|
||||
end else
|
||||
Result := False;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropCommon.ListClick(Sender: TObject);
|
||||
begin
|
||||
//No need to call fOnChange here since ListChange was already called
|
||||
if fAutoClose then ListHide(nil);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropCommon.ListChange(Sender: TObject);
|
||||
begin
|
||||
if (ItemIndex <> -1) then
|
||||
if Assigned(fOnChange) then fOnChange(Self);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropCommon.ListHide(Sender: TObject);
|
||||
begin
|
||||
fShape.Hide;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropCommon.SetDropCount(const aValue: Integer);
|
||||
begin
|
||||
fDropCount := aValue;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropCommon.SetEnabled(aValue: Boolean);
|
||||
begin
|
||||
inherited;
|
||||
fButton.Enabled := Enabled;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropCommon.OpenList;
|
||||
begin
|
||||
ListShow(nil);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropCommon.CloseList;
|
||||
begin
|
||||
ListHide(nil);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropCommon.SetOpenList(aOpen: Boolean);
|
||||
begin
|
||||
if aOpen then
|
||||
OpenList
|
||||
else
|
||||
CloseList;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropCommon.SwitchOpen;
|
||||
begin
|
||||
if IsOpen then
|
||||
CloseList
|
||||
else
|
||||
OpenList;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropCommon.SetLeft(aValue: Integer);
|
||||
begin
|
||||
inherited;
|
||||
// Stick the button to us
|
||||
fButton.Left := Left + Width - Height;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropCommon.SetTop(aValue: Integer);
|
||||
begin
|
||||
inherited;
|
||||
// Stick the button to us
|
||||
fButton.Top := Top;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropCommon.SetVisible(aValue: Boolean);
|
||||
begin
|
||||
inherited;
|
||||
fButton.Visible := aValue;
|
||||
if not aValue then ListHide(Self);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropCommon.Paint;
|
||||
begin
|
||||
inherited;
|
||||
|
||||
TKMRenderUI.WriteBevel(AbsLeft, AbsTop, Width, Height);
|
||||
|
||||
// Make sure the list stays where it needs to be relative DropBox (e.g. on window resize)
|
||||
UpdateDropPosition;
|
||||
end;
|
||||
|
||||
|
||||
{ TKMDropList }
|
||||
constructor TKMDropList.Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; aFont: TKMFont; const aDefaultCaption: UnicodeString;
|
||||
aStyle: TKMButtonStyle; aAutoClose: Boolean = True; aBackAlpha: Single = 0.85);
|
||||
begin
|
||||
inherited Create(aParent, aLeft, aTop, aWidth, aHeight, aFont, aStyle, aAutoClose);
|
||||
|
||||
fDefaultCaption := aDefaultCaption;
|
||||
|
||||
fListTopIndex := 0;
|
||||
|
||||
fList := TKMListBox.Create(MasterParent, 0, 0, aWidth, 0, fFont, aStyle);
|
||||
fList.Height := fList.ItemHeight * fDropCount;
|
||||
fList.AutoHideScrollBar := True; //A drop box should only have a scrollbar if required
|
||||
fList.BackAlpha := aBackAlpha;
|
||||
fList.OnClick := ListClick;
|
||||
fList.OnChange := ListChange;
|
||||
|
||||
DropWidth := aWidth;
|
||||
|
||||
ListHide(nil);
|
||||
fList.OnKeyDown := ListKeyDown;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropList.ListShow(Sender: TObject);
|
||||
begin
|
||||
inherited;
|
||||
if ListVisible or (Count < 1) then Exit;
|
||||
|
||||
//Make sure the selected item is visible when list is opened
|
||||
if (ItemIndex <> -1) then
|
||||
begin
|
||||
if InRange(ItemIndex, fListTopIndex, fListTopIndex + fList.GetVisibleRows) then //Try to open list at previously saved scroll position
|
||||
fList.TopIndex := fListTopIndex
|
||||
else
|
||||
fList.TopIndex := ItemIndex;
|
||||
end;
|
||||
|
||||
fList.Show;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropList.ListClick(Sender: TObject);
|
||||
begin
|
||||
fCaption := fList.Item[ItemIndex];
|
||||
|
||||
inherited;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropList.ListChange(Sender: TObject);
|
||||
begin
|
||||
fCaption := fList.Item[ItemIndex];
|
||||
|
||||
inherited;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropList.ListHide(Sender: TObject);
|
||||
begin
|
||||
fListTopIndex := fList.TopIndex; //Save list scroll position
|
||||
inherited;
|
||||
fList.Hide;
|
||||
end;
|
||||
|
||||
|
||||
function TKMDropList.ListVisible: Boolean;
|
||||
begin
|
||||
Result := fList.Visible;
|
||||
end;
|
||||
|
||||
|
||||
function TKMDropList.GetItemIndex: Smallint;
|
||||
begin
|
||||
Result := fList.ItemIndex;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropList.SetItemIndex(aIndex: Smallint);
|
||||
begin
|
||||
fList.ItemIndex := aIndex;
|
||||
if aIndex <> -1 then
|
||||
fCaption := fList.Item[fList.ItemIndex]
|
||||
else
|
||||
fCaption := fDefaultCaption;
|
||||
end;
|
||||
|
||||
|
||||
function TKMDropList.GetShowHintWhenShort: Boolean;
|
||||
begin
|
||||
Result := fList.ShowHintWhenShort;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropList.SetShowHintWhenShort(const aValue: Boolean);
|
||||
begin
|
||||
fList.ShowHintWhenShort := aValue;
|
||||
end;
|
||||
|
||||
|
||||
function TKMDropList.IsOpen: Boolean;
|
||||
begin
|
||||
Result := fList.Visible;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropList.SetDropCount(const aValue: Integer);
|
||||
begin
|
||||
inherited;
|
||||
|
||||
fList.Height := fList.ItemHeight * fDropCount;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropList.SetDropWidth(aDropWidth: Integer);
|
||||
begin
|
||||
fDropWidth := aDropWidth;
|
||||
fList.AbsLeft := AbsLeft + Width - aDropWidth;
|
||||
fList.Width := aDropWidth;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropList.SetEnabled(aValue: Boolean);
|
||||
begin
|
||||
inherited;
|
||||
fList.Enabled := Enabled;
|
||||
if not Enabled and fList.Visible then
|
||||
ListHide(nil);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropList.SetVisible(aValue: Boolean);
|
||||
begin
|
||||
inherited;
|
||||
if not aValue then ListHide(Self);
|
||||
end;
|
||||
|
||||
|
||||
function TKMDropList.GetCount: Integer;
|
||||
begin
|
||||
Result := fList.Count;
|
||||
end;
|
||||
|
||||
|
||||
// When new items are added to the list we must update the drop height and position
|
||||
procedure TKMDropList.UpdateDropPosition;
|
||||
begin
|
||||
if Count > 0 then
|
||||
begin
|
||||
fList.Height := Math.Min(fDropCount, fList.Count) * fList.ItemHeight + fList.SeparatorsCount*fList.SeparatorHeight;
|
||||
|
||||
if fDropUp then
|
||||
fList.AbsTop := AbsTop - fList.Height
|
||||
else
|
||||
fList.AbsTop := AbsTop + Height;
|
||||
|
||||
fList.Left := AbsLeft + Width - DropWidth - MasterParent.AbsLeft;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropList.Add(const aItem: UnicodeString; aTag: Integer = 0);
|
||||
begin
|
||||
fList.Add(aItem, aTag);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropList.SelectByName(const aText: UnicodeString);
|
||||
var I: Integer;
|
||||
begin
|
||||
fList.ItemIndex := -1;
|
||||
for I := 0 to fList.Count - 1 do
|
||||
if fList.Item[I] = aText then
|
||||
SetItemIndex(I);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropList.SelectByTag(aTag: Integer);
|
||||
var I: Integer;
|
||||
begin
|
||||
fList.ItemIndex := -1;
|
||||
for I := 0 to fList.Count - 1 do
|
||||
if fList.ItemTags[I] = aTag then
|
||||
SetItemIndex(I);
|
||||
end;
|
||||
|
||||
|
||||
function TKMDropList.HasTag(aTag: Integer): Boolean;
|
||||
var
|
||||
I: Integer;
|
||||
begin
|
||||
Result := False;
|
||||
for I := 0 to fList.Count - 1 do
|
||||
if fList.ItemTags[I] = aTag then
|
||||
Exit(True);
|
||||
end;
|
||||
|
||||
|
||||
function TKMDropList.IsSelected: Boolean;
|
||||
begin
|
||||
Result := fList.ItemIndex <> -1;
|
||||
end;
|
||||
|
||||
|
||||
function TKMDropList.GetTag(aIndex: Integer): Integer;
|
||||
begin
|
||||
Result := fList.ItemTags[aIndex];
|
||||
end;
|
||||
|
||||
|
||||
function TKMDropList.GetSelectedTag: Integer;
|
||||
begin
|
||||
Result := GetTag(fList.ItemIndex);
|
||||
end;
|
||||
|
||||
function TKMDropList.GetItem(aIndex: Integer): UnicodeString;
|
||||
begin
|
||||
Result := fList.Item[aIndex];
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropList.Clear;
|
||||
begin
|
||||
fList.Clear;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropList.Paint;
|
||||
var
|
||||
col: TColor4;
|
||||
begin
|
||||
inherited;
|
||||
|
||||
if Enabled then
|
||||
col := icWhite
|
||||
else
|
||||
col := icGray2;
|
||||
|
||||
TKMRenderUI.WriteText(AbsLeft+4, AbsTop+4, Width-8, fCaption, fFont, taLeft, col);
|
||||
end;
|
||||
|
||||
|
||||
{ TKMDropColumns }
|
||||
constructor TKMDropColumns.Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; aFont: TKMFont; const aDefaultCaption: UnicodeString; aStyle: TKMButtonStyle; aShowHeader: Boolean = True);
|
||||
begin
|
||||
inherited Create(aParent, aLeft, aTop, aWidth, aHeight, aFont, aStyle);
|
||||
|
||||
fListTopIndex := 0;
|
||||
|
||||
fDefaultCaption := aDefaultCaption;
|
||||
|
||||
fList := TKMColumnBox.Create(MasterParent, 0, 0, aWidth, 0, fFont, aStyle);
|
||||
fList.BackAlpha := 0.85;
|
||||
fList.OnClick := ListClick;
|
||||
fList.OnChange := ListChange;
|
||||
fList.ShowHeader := aShowHeader;
|
||||
|
||||
DropWidth := aWidth;
|
||||
|
||||
ListHide(nil);
|
||||
fList.OnKeyDown := ListKeyDown;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropColumns.ListShow(Sender: TObject);
|
||||
begin
|
||||
inherited;
|
||||
if ListVisible or (Count < 1) then Exit;
|
||||
|
||||
//Make sure the selected item is visible when list is opened
|
||||
if (ItemIndex <> -1) then
|
||||
begin
|
||||
if InRange(ItemIndex, fListTopIndex, fListTopIndex + fList.GetVisibleRows) then //Try to open list at previously saved scroll position
|
||||
fList.TopIndex := fListTopIndex
|
||||
else
|
||||
fList.TopIndex := ItemIndex;
|
||||
end;
|
||||
|
||||
fList.Show;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropColumns.ListClick(Sender: TObject);
|
||||
begin
|
||||
inherited;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropColumns.ListChange(Sender: TObject);
|
||||
begin
|
||||
inherited;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropColumns.ListHide(Sender: TObject);
|
||||
begin
|
||||
fListTopIndex := fList.TopIndex; //Save list scroll position
|
||||
inherited;
|
||||
fList.Hide;
|
||||
end;
|
||||
|
||||
|
||||
function TKMDropColumns.ListVisible: Boolean;
|
||||
begin
|
||||
Result := fList.Visible;
|
||||
end;
|
||||
|
||||
|
||||
function TKMDropColumns.GetItemIndex: smallint;
|
||||
begin
|
||||
Result := fList.ItemIndex;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropColumns.SetItemIndex(aIndex: smallint);
|
||||
begin
|
||||
fList.ItemIndex := aIndex;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropColumns.SetDropWidth(aDropWidth: Integer);
|
||||
begin
|
||||
fDropWidth := aDropWidth;
|
||||
fList.AbsLeft := AbsLeft + Width - aDropWidth;
|
||||
fList.Width := aDropWidth;
|
||||
end;
|
||||
|
||||
|
||||
function TKMDropColumns.GetShowHintWhenShort: Boolean;
|
||||
begin
|
||||
Result := fList.ShowHintWhenShort
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropColumns.SetShowHintWhenShort(const aValue: Boolean);
|
||||
begin
|
||||
fList.ShowHintWhenShort := aValue;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropColumns.SetColumns(aFont: TKMFont; aColumns: array of string; aColumnOffsets: array of Word);
|
||||
var
|
||||
I: Integer;
|
||||
columnsToShowWhenListHidden: array of Boolean;
|
||||
begin
|
||||
SetLength(columnsToShowWhenListHidden, Length(aColumns));
|
||||
for I := Low(columnsToShowWhenListHidden) to High(columnsToShowWhenListHidden) do
|
||||
columnsToShowWhenListHidden[I] := True; // by default show all columns
|
||||
SetColumns(aFont, aColumns, aColumnOffsets, columnsToShowWhenListHidden);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropColumns.SetColumns(aFont: TKMFont; aColumns: array of string; aColumnOffsets: array of Word; aColumnsToShowWhenListHidden: array of Boolean);
|
||||
var
|
||||
I: Integer;
|
||||
begin
|
||||
Assert(Length(aColumns) = Length(aColumnsToShowWhenListHidden));
|
||||
fList.SetColumns(aFont, aColumns, aColumnOffsets);
|
||||
SetLength(fColumnsToShowWhenListHidden, Length(aColumnsToShowWhenListHidden));
|
||||
for I := Low(aColumnsToShowWhenListHidden) to High(aColumnsToShowWhenListHidden) do
|
||||
fColumnsToShowWhenListHidden[I] := aColumnsToShowWhenListHidden[I];
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropColumns.SetEnabled(aValue: Boolean);
|
||||
begin
|
||||
inherited;
|
||||
fList.Enabled := Enabled;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropColumns.SetVisible(aValue: Boolean);
|
||||
begin
|
||||
inherited;
|
||||
if not aValue then ListHide(Self);
|
||||
end;
|
||||
|
||||
|
||||
function TKMDropColumns.GetCount: Integer;
|
||||
begin
|
||||
Result := fList.RowCount;
|
||||
end;
|
||||
|
||||
|
||||
//When new items are added to the list we must update the drop height and position
|
||||
procedure TKMDropColumns.UpdateDropPosition;
|
||||
begin
|
||||
if Count > 0 then
|
||||
begin
|
||||
fList.Height := Math.Min(fDropCount, fList.RowCount) * fList.ItemHeight + fList.Header.Height * Ord(fList.ShowHeader);
|
||||
|
||||
if fDropUp then
|
||||
fList.AbsTop := AbsTop - fList.Height
|
||||
else
|
||||
fList.AbsTop := AbsTop + Height;
|
||||
|
||||
fList.Left := AbsLeft + Width - DropWidth - MasterParent.AbsLeft;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropColumns.Add(aItem: TKMListRow);
|
||||
begin
|
||||
fList.AddItem(aItem);
|
||||
end;
|
||||
|
||||
|
||||
function TKMDropColumns.GetItem(aIndex: Integer): TKMListRow;
|
||||
begin
|
||||
Result := fList.Rows[aIndex];
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropColumns.Clear;
|
||||
begin
|
||||
fList.Clear;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMDropColumns.Paint;
|
||||
var
|
||||
col: TColor4;
|
||||
begin
|
||||
inherited;
|
||||
|
||||
if Enabled then
|
||||
col := icWhite
|
||||
else
|
||||
col := icGray2;
|
||||
|
||||
if ItemIndex = -1 then
|
||||
TKMRenderUI.WriteText(AbsLeft + 4, AbsTop + 4, Width - 8 - fButton.Width, fDefaultCaption, fFont, taLeft, col)
|
||||
else
|
||||
fList.DoPaintLine(ItemIndex, AbsLeft, AbsTop, Width - fButton.Width, fColumnsToShowWhenListHidden, False);
|
||||
end;
|
||||
|
||||
|
||||
end.
|
||||
+1050
File diff suppressed because it is too large.
Load diff
+350
@@ -0,0 +1,350 @@
|
||||
unit KM_ControlsForm;
|
||||
{$I KaM_Remake.inc}
|
||||
interface
|
||||
uses
|
||||
Classes, Controls,
|
||||
KM_Controls, KM_ControlsBase,
|
||||
KM_CommonTypes, KM_Points, KM_ResFonts;
|
||||
|
||||
|
||||
type
|
||||
TKMFormBackgroundType = (
|
||||
fbGray, // Dark grey scroll with big header roll
|
||||
fbYellow, // Yellow scroll without header roll
|
||||
fbScroll // Yellow scroll with small header roll
|
||||
);
|
||||
|
||||
TKMForm = class(TKMPanel)
|
||||
const
|
||||
DEFAULT_CAPTION_FONT = fntOutline;
|
||||
private
|
||||
fDragging: Boolean;
|
||||
fDragStartPos: TKMPoint;
|
||||
fBackground: TKMFormBackgroundType;
|
||||
fHandleCloseKey: Boolean;
|
||||
fCapOffsetY: Integer;
|
||||
|
||||
fOnClose: TKMEvent;
|
||||
procedure UpdateSizes;
|
||||
procedure Close(Sender: TObject);
|
||||
|
||||
procedure HandleOtherControlMouseMove(Sender: TObject; X,Y: Integer; Shift: TShiftState);
|
||||
procedure HandleOtherControlMouseDown(Sender: TObject; X,Y: Integer; Shift: TShiftState; Button: TMouseButton);
|
||||
procedure HandleOtherControlMouseUp(Sender: TObject; X,Y: Integer; Shift: TShiftState; Button: TMouseButton);
|
||||
|
||||
function MarginMainLeftRight: Integer;
|
||||
function MarginMainTop: Integer;
|
||||
function MarginMainBottom: Integer;
|
||||
function MarginCrossTop: Integer;
|
||||
function MarginCrossRight: Integer;
|
||||
|
||||
function GetActualWidth: Integer;
|
||||
procedure SetActualWidth(aValue: Integer);
|
||||
function GetActualHeight: Integer;
|
||||
procedure SetActualHeight(aValue: Integer);
|
||||
|
||||
procedure SetHandleCloseKey(aValue: Boolean);
|
||||
procedure SetCapOffsetY(aValue: Integer);
|
||||
function GetCaption: string;
|
||||
procedure SetCaption(const aValue: string);
|
||||
protected
|
||||
Bevel_Contents: TKMBevel;
|
||||
Bevel_ModalBackground: TKMBevel;
|
||||
Image_Background, Image_Close: TKMImage;
|
||||
Label_Caption: TKMLabel;
|
||||
procedure SetWidth(aValue: Integer); override;
|
||||
procedure SetHeight(aValue: Integer); override;
|
||||
public
|
||||
ItemsPanel: TKMPanel;
|
||||
DragEnabled: Boolean;
|
||||
|
||||
constructor Create(aParent: TKMPanel; aContentWidth, aContentHeight: Integer; const aCaption: UnicodeString = '';
|
||||
aBackground: TKMFormBackgroundType = fbYellow; aCloseIcon: Boolean = False;
|
||||
aBevelForContents: Boolean = True; aModalBackground: Boolean = True);
|
||||
|
||||
procedure MouseDown (X,Y: Integer; Shift: TShiftState; Button: TMouseButton); override;
|
||||
procedure MouseMove (X,Y: Integer; Shift: TShiftState); override;
|
||||
procedure MouseUp (X,Y: Integer; Shift: TShiftState; Button: TMouseButton); override;
|
||||
|
||||
function KeyUp(Key: Word; Shift: TShiftState): Boolean; override;
|
||||
|
||||
property OnClose: TKMEvent read fOnClose write fOnClose;
|
||||
|
||||
property ActualHeight: Integer read GetActualHeight write SetActualHeight;
|
||||
property ActualWidth: Integer read GetActualWidth write SetActualWidth;
|
||||
property CapOffsetY: Integer read fCapOffsetY write SetCapOffsetY;
|
||||
property Caption: string read GetCaption write SetCaption;
|
||||
|
||||
property HandleCloseKey: Boolean read fHandleCloseKey write SetHandleCloseKey;
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
uses
|
||||
Math,
|
||||
KM_RenderUI,
|
||||
KM_ResTexts, KM_ResKeys, KM_ResTypes;
|
||||
|
||||
|
||||
{ TKMForm }
|
||||
constructor TKMForm.Create(aParent: TKMPanel; aContentWidth, aContentHeight: Integer; const aCaption: UnicodeString = '';
|
||||
aBackground: TKMFormBackgroundType = fbYellow; aCloseIcon: Boolean = False;
|
||||
aBevelForContents: Boolean = True; aModalBackground: Boolean = True);
|
||||
const
|
||||
BG_RX: array [TKMFormBackgroundType] of TRXType = (rxGuiMain, rxGuiMain, rxGui);
|
||||
BG_ID: array [TKMFormBackgroundType] of Word = (15, 18, 409);
|
||||
begin
|
||||
fBackground := aBackground;
|
||||
|
||||
var desiredWidth := aContentWidth + 2 * MarginMainLeftRight;
|
||||
var desiredHeight := aContentHeight + MarginMainBottom + MarginMainTop;
|
||||
var desiredLeft := Max(0, (aParent.Width - desiredWidth) div 2);
|
||||
var desiredTop := Max(0, (aParent.Height - desiredHeight) div 2);
|
||||
|
||||
// Create panel with calculated sizes
|
||||
inherited Create(aParent, desiredLeft, desiredTop, desiredWidth, desiredHeight);
|
||||
|
||||
// Fix its base sizes as a desired one
|
||||
BaseWidth := desiredWidth;
|
||||
BaseHeight := desiredHeight;
|
||||
|
||||
// FitInParent := True; not sure if this is ever needed
|
||||
DragEnabled := False;
|
||||
fHandleCloseKey := False;
|
||||
fCapOffsetY := 0;
|
||||
|
||||
if aModalBackground then
|
||||
Bevel_ModalBackground := TKMBevel.Create(Self, -5000, -5000, 10000, 10000);
|
||||
|
||||
Image_Background := TKMImage.Create(Self, 0, 0, desiredWidth, desiredHeight, BG_ID[fBackground], BG_RX[fBackground]);
|
||||
Image_Background.AnchorsStretch;
|
||||
Image_Background.ImageStretch;
|
||||
|
||||
if aCloseIcon then
|
||||
begin
|
||||
Image_Close := TKMImage.Create(Self, Width - MarginCrossRight, MarginCrossTop, 31, 30, 52);
|
||||
Image_Close.Anchors := [anTop, anRight];
|
||||
Image_Close.Hint := gResTexts[TX_MSG_CLOSE_HINT];
|
||||
Image_Close.OnClick := Close;
|
||||
Image_Close.HighlightOnMouseOver := True;
|
||||
end;
|
||||
|
||||
ItemsPanel := TKMPanel.Create(Self, MarginMainLeftRight, MarginMainTop, Width - 2*MarginMainLeftRight, Height - MarginMainTop - MarginMainBottom);
|
||||
ItemsPanel.AnchorsStretch;
|
||||
if aBevelForContents then
|
||||
begin
|
||||
Bevel_Contents := TKMBevel.Create(ItemsPanel, 0, 0, ItemsPanel.Width, ItemsPanel.Height);
|
||||
Bevel_Contents.AnchorsStretch;
|
||||
end;
|
||||
|
||||
Label_Caption := TKMLabel.Create(ItemsPanel, 0, -25, ItemsPanel.Width, 20, aCaption, DEFAULT_CAPTION_FONT, taCenter);
|
||||
|
||||
AnchorsCenter;
|
||||
Hide;
|
||||
|
||||
fMasterControl.SubscribeOnOtherMouseMove(HandleOtherControlMouseMove);
|
||||
fMasterControl.SubscribeOnOtherMouseDown(HandleOtherControlMouseDown);
|
||||
fMasterControl.SubscribeOnOtherMouseUp(HandleOtherControlMouseUp);
|
||||
end;
|
||||
|
||||
|
||||
function TKMForm.MarginMainLeftRight: Integer;
|
||||
const
|
||||
MARGIN_SIDE: array [TKMFormBackgroundType] of Byte = (20, 35, 20);
|
||||
begin
|
||||
Result := MARGIN_SIDE[fBackground];
|
||||
end;
|
||||
|
||||
|
||||
function TKMForm.MarginMainTop: Integer;
|
||||
const
|
||||
MARGIN_TOP: array [TKMFormBackgroundType] of Byte = (40, 80, 50);
|
||||
begin
|
||||
Result := MARGIN_TOP[fBackground];
|
||||
end;
|
||||
|
||||
|
||||
function TKMForm.MarginMainBottom: Integer;
|
||||
const
|
||||
MARGIN_BOTTOM: array [TKMFormBackgroundType] of Byte = (20, 50, 20);
|
||||
begin
|
||||
Result := MARGIN_BOTTOM[fBackground];
|
||||
end;
|
||||
|
||||
|
||||
function TKMForm.MarginCrossTop: Integer;
|
||||
const
|
||||
CROSS_TOP: array [TKMFormBackgroundType] of Byte = (24, 40, 24);
|
||||
begin
|
||||
Result := CROSS_TOP[fBackground];
|
||||
end;
|
||||
|
||||
|
||||
function TKMForm.MarginCrossRight: Integer;
|
||||
const
|
||||
// Margin from right side, depends on bg graphics
|
||||
CROSS_RIGHT: array [TKMFormBackgroundType] of Byte = (50, 130, 55);
|
||||
begin
|
||||
Result := CROSS_RIGHT[fBackground];
|
||||
end;
|
||||
|
||||
|
||||
function TKMForm.GetCaption: string;
|
||||
begin
|
||||
Result := Label_Caption.Caption;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMForm.Close(Sender: TObject);
|
||||
begin
|
||||
Hide;
|
||||
|
||||
if Assigned(fOnClose) then
|
||||
fOnClose;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMForm.HandleOtherControlMouseDown(Sender: TObject; X,Y: Integer; Shift: TShiftState; Button: TMouseButton);
|
||||
begin
|
||||
if Sender = Image_Background then
|
||||
MouseDown(X, Y, Shift, Button);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMForm.HandleOtherControlMouseMove(Sender: TObject; X, Y: Integer; Shift: TShiftState);
|
||||
begin
|
||||
inherited;
|
||||
|
||||
MouseMove(X, Y, Shift);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMForm.HandleOtherControlMouseUp(Sender: TObject; X,Y: Integer; Shift: TShiftState; Button: TMouseButton);
|
||||
begin
|
||||
MouseUp(X, Y, Shift, Button);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMForm.MouseDown(X, Y: Integer; Shift: TShiftState; Button: TMouseButton);
|
||||
begin
|
||||
inherited;
|
||||
|
||||
if not DragEnabled then Exit;
|
||||
|
||||
fDragging := True;
|
||||
fDragStartPos := TKMPoint.New(X,Y);
|
||||
end;
|
||||
|
||||
procedure TKMForm.MouseMove(X, Y: Integer; Shift: TShiftState);
|
||||
begin
|
||||
inherited;
|
||||
|
||||
if not DragEnabled or not fDragging then Exit;
|
||||
|
||||
Left := EnsureRange(Left + X - fDragStartPos.X, 0, fMasterControl.MasterPanel.Width - Width);
|
||||
Top := EnsureRange(Top + Y - fDragStartPos.Y, -Image_Background.Top, fMasterControl.MasterPanel.Height - Height);
|
||||
|
||||
fDragStartPos := TKMPoint.New(X,Y);
|
||||
end;
|
||||
|
||||
procedure TKMForm.MouseUp(X, Y: Integer; Shift: TShiftState; Button: TMouseButton);
|
||||
begin
|
||||
inherited;
|
||||
|
||||
if not DragEnabled then Exit;
|
||||
|
||||
fDragging := False;
|
||||
end;
|
||||
|
||||
|
||||
function TKMForm.KeyUp(Key: Word; Shift: TShiftState): Boolean;
|
||||
begin
|
||||
Result := inherited;
|
||||
if Result then Exit; // Key already handled
|
||||
|
||||
if not fHandleCloseKey then Exit;
|
||||
|
||||
if Key = gResKeys[kfCloseMenu] then
|
||||
begin
|
||||
Close(nil);
|
||||
Result := True;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMForm.SetHeight(aValue: Integer);
|
||||
begin
|
||||
inherited;
|
||||
|
||||
UpdateSizes;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMForm.SetWidth(aValue: Integer);
|
||||
begin
|
||||
inherited;
|
||||
|
||||
UpdateSizes;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMForm.UpdateSizes;
|
||||
begin
|
||||
// Reposition buttons?
|
||||
end;
|
||||
|
||||
|
||||
function TKMForm.GetActualWidth: Integer;
|
||||
begin
|
||||
Result := ItemsPanel.Width;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMForm.SetActualWidth(aValue: Integer);
|
||||
var
|
||||
baseW: Integer;
|
||||
begin
|
||||
baseW := aValue + MarginMainLeftRight*2;
|
||||
SetWidth(Min(Parent.Width, baseW));
|
||||
end;
|
||||
|
||||
|
||||
function TKMForm.GetActualHeight: Integer;
|
||||
begin
|
||||
Result := ItemsPanel.Height;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMForm.SetActualHeight(aValue: Integer);
|
||||
var
|
||||
baseH, h: Integer;
|
||||
begin
|
||||
baseH := aValue + MarginMainBottom + MarginMainTop;
|
||||
h := Min(Parent.Height, baseH);
|
||||
SetHeight(h);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMForm.SetHandleCloseKey(aValue: Boolean);
|
||||
begin
|
||||
fHandleCloseKey := aValue;
|
||||
Focusable := aValue;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMForm.SetCapOffsetY(aValue: Integer);
|
||||
begin
|
||||
Label_Caption.Top := Label_Caption.Top + aValue - fCapOffsetY;
|
||||
|
||||
fCapOffsetY := aValue;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMForm.SetCaption(const aValue: string);
|
||||
begin
|
||||
Label_Caption.Caption := aValue;
|
||||
end;
|
||||
|
||||
|
||||
end.
|
||||
|
||||
+2093
File diff suppressed because it is too large.
Load diff
+783
@@ -0,0 +1,783 @@
|
||||
unit KM_ControlsMemo;
|
||||
{$I KaM_Remake.inc}
|
||||
interface
|
||||
uses
|
||||
Classes, Controls,
|
||||
KM_Controls, KM_ControlsScroll,
|
||||
KM_RenderUI, KM_ResFonts,
|
||||
KM_CommonTypes, KM_Points;
|
||||
|
||||
|
||||
type
|
||||
TKMMemo = class(TKMControl)
|
||||
private
|
||||
fFont: TKMFont; //Should not be changed from inital value, it will mess up the word wrapping
|
||||
fItemHeight: Byte;
|
||||
fItems: TStringList;
|
||||
fWordWrap: Boolean;
|
||||
fIndentAfterNL: Boolean;
|
||||
fText: UnicodeString;
|
||||
fScrollDown: Boolean;
|
||||
fScrollBar: TKMScrollBar;
|
||||
fOnChange: TNotifyEvent;
|
||||
fCursorPos: Integer;
|
||||
fSelectable: Boolean;
|
||||
fSelectionStart: Integer;
|
||||
fSelectionEnd: Integer;
|
||||
fSelectionInitialPos: Integer;
|
||||
|
||||
procedure SetWordWrap(const Value: Boolean);
|
||||
function GetText: UnicodeString;
|
||||
procedure SetText(const aText: UnicodeString);
|
||||
function GetTopIndex: smallint;
|
||||
procedure SetTopIndex(aIndex: SmallInt);
|
||||
procedure ReformatText;
|
||||
procedure UpdateScrollBar;
|
||||
|
||||
function KeyEventHandled(Key: Word; Shift: TShiftState): Boolean;
|
||||
|
||||
procedure SetCursorPos(aPos: Integer);
|
||||
function LinearToPointPos(aPos: Integer): TKMPoint;
|
||||
function PointToLinearPos(aColumn, aRow: Integer): Integer;
|
||||
function GetCharPosAt(X,Y: Integer): TKMPoint;
|
||||
function GetCursorPosAt(X,Y: Integer): Integer;
|
||||
procedure ResetSelection;
|
||||
function HasSelection: Boolean;
|
||||
function GetPlainText(aUsePipeAsEOL: Boolean; aStart: Integer = -1; aEnd: Integer = -1): UnicodeString;
|
||||
function GetSelectedText: UnicodeString;
|
||||
function GetMaxCursorPos: Integer;
|
||||
function GetMaxPosInRow(aRow: Integer): Integer;
|
||||
function GetMinPosInRow(aRow: Integer): Integer;
|
||||
function GetMaxCursorPosInRow: Integer;
|
||||
function GetMinCursorPosInRow: Integer;
|
||||
procedure SetSelectionStart(aValue: Integer);
|
||||
procedure SetSelectionEnd(aValue: Integer);
|
||||
procedure SetSelections(aValue1, aValue2: Integer);
|
||||
procedure UpdateSelection(aOldCursorPos: Integer; aIsSelecting: Boolean);
|
||||
protected
|
||||
procedure SetHeight(aValue: Integer); override;
|
||||
procedure SetWidth(aValue: Integer); override;
|
||||
procedure SetVisible(aValue: Boolean); override;
|
||||
procedure SetEnabled(aValue: Boolean); override;
|
||||
procedure SetTop(aValue: Integer); override;
|
||||
procedure FocusChanged(aFocused: Boolean); override;
|
||||
procedure ControlMouseDown(Sender: TObject; X,Y: Integer; Shift: TShiftState; Button: TMouseButton);
|
||||
function DoHandleMouseWheelByDefault: Boolean; override;
|
||||
public
|
||||
constructor Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; aFont: TKMFont; aStyle: TKMButtonStyle; aSelectable: Boolean = True);
|
||||
destructor Destroy; override;
|
||||
|
||||
procedure Add(const aItem: UnicodeString);
|
||||
procedure Clear;
|
||||
procedure ScrollToBottom;
|
||||
property WordWrap: Boolean read fWordWrap write SetWordWrap; //Whether to automatically wrap text within given text area width
|
||||
property IndentAfterNL: Boolean read fIndentAfterNL write fIndentAfterNL;
|
||||
property Text: UnicodeString read GetText write SetText;
|
||||
property ItemHeight: Byte read fItemHeight write fItemHeight;
|
||||
property TopIndex: Smallint read GetTopIndex write SetTopIndex;
|
||||
property ScrollDown: Boolean read fScrollDown write fScrollDown;
|
||||
property CursorPos: Integer read fCursorPos write SetCursorPos;
|
||||
property Selectable: Boolean read fSelectable write fSelectable;
|
||||
property SelectionStart: Integer read fSelectionStart write SetSelectionStart;
|
||||
property SelectionEnd: Integer read fSelectionEnd write SetSelectionEnd;
|
||||
|
||||
function GetVisibleRows: Integer;
|
||||
function KeyDown(Key: Word; Shift: TShiftState): Boolean; override;
|
||||
function KeyUp(Key: Word; Shift: TShiftState): Boolean; override;
|
||||
procedure MouseWheel(Sender: TObject; WheelSteps: Integer; var aHandled: Boolean); override;
|
||||
procedure MouseDown(X,Y: Integer; Shift: TShiftState; Button: TMouseButton); override;
|
||||
procedure MouseMove(X,Y: Integer; Shift: TShiftState); override;
|
||||
procedure MouseUp(X,Y: Integer; Shift: TShiftState; Button: TMouseButton); override;
|
||||
|
||||
property OnChange: TNotifyEvent read fOnChange write fOnChange;
|
||||
|
||||
procedure Paint; override;
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
uses
|
||||
KM_Defaults,
|
||||
{$IFDEF MSWindows} Windows, {$ENDIF}
|
||||
{$IFDEF Unix} LCLIntf, LCLType, {$ENDIF}
|
||||
SysUtils, StrUtils, Math,
|
||||
Clipbrd,
|
||||
KromUtils,
|
||||
KM_ControlsUtils,
|
||||
KM_Resource, KM_Render, KM_RenderTypes,
|
||||
KM_CommonUtils;
|
||||
|
||||
|
||||
{ TKMMemo }
|
||||
constructor TKMMemo.Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; aFont: TKMFont; aStyle: TKMButtonStyle; aSelectable: Boolean = True);
|
||||
begin
|
||||
inherited Create(aParent, aLeft, aTop, aWidth, aHeight);
|
||||
fItemHeight := 20;
|
||||
fItems := TStringList.Create;
|
||||
fFont := aFont;
|
||||
|
||||
fScrollBar := TKMScrollBar.Create(aParent, aLeft+aWidth-20, aTop, 20, aHeight, saVertical, aStyle);
|
||||
UpdateScrollBar; //Initialise the scrollbar
|
||||
fSelectable := aSelectable;
|
||||
|
||||
aParent.MasterControl.SubscribeOnOtherMouseDown(ControlMouseDown);
|
||||
end;
|
||||
|
||||
|
||||
destructor TKMMemo.Destroy;
|
||||
begin
|
||||
fItems.Free;
|
||||
inherited;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMMemo.SetHeight(aValue: Integer);
|
||||
begin
|
||||
inherited;
|
||||
fScrollBar.Height := Height;
|
||||
UpdateScrollBar; //Since height has changed
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMMemo.SetWidth(aValue: Integer);
|
||||
begin
|
||||
inherited;
|
||||
fScrollBar.Left := Left + Width - fScrollBar.Width;
|
||||
ReformatText; //Repositions the scroll bar as well
|
||||
end;
|
||||
|
||||
|
||||
//Copy property to scrollbar. Otherwise it won't be rendered
|
||||
procedure TKMMemo.SetVisible(aValue: Boolean);
|
||||
begin
|
||||
inherited;
|
||||
|
||||
//Hide scrollbar and its buttons
|
||||
fScrollBar.Visible := IsSetVisible and (fScrollBar.MaxValue <> fScrollBar.MinValue);
|
||||
end;
|
||||
|
||||
|
||||
function TKMMemo.GetTopIndex: smallint;
|
||||
begin
|
||||
Result := fScrollBar.Position;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMMemo.SetTopIndex(aIndex: smallint);
|
||||
begin
|
||||
fScrollBar.Position := aIndex;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMMemo.SetEnabled(aValue: Boolean);
|
||||
begin
|
||||
inherited;
|
||||
fScrollBar.Enabled := Enabled;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMMemo.SetTop(aValue: Integer);
|
||||
begin
|
||||
inherited;
|
||||
fScrollBar.Top := Top;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMMemo.SetWordWrap(const Value: boolean);
|
||||
begin
|
||||
fWordWrap := Value;
|
||||
ReformatText;
|
||||
end;
|
||||
|
||||
|
||||
function TKMMemo.GetText: UnicodeString;
|
||||
begin
|
||||
Result := fText;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMMemo.SetText(const aText: UnicodeString);
|
||||
begin
|
||||
fText := aText;
|
||||
ReformatText;
|
||||
|
||||
if Assigned(fOnChange) then
|
||||
fOnChange(Self);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMMemo.ReformatText;
|
||||
var
|
||||
newText: UnicodeString;
|
||||
begin
|
||||
if fWordWrap then
|
||||
newText := gRes.Fonts[fFont].WordWrap(fText, Width - fScrollBar.Width - 8, True, IndentAfterNL)
|
||||
else
|
||||
newText := fText;
|
||||
|
||||
//KaM uses | for new line, fItems.Text:= uses standard eol to parse each item from the string
|
||||
fItems.Text := StringReplace(newText, '|', EolW, [rfReplaceAll]);
|
||||
UpdateScrollBar;
|
||||
end;
|
||||
|
||||
|
||||
function TKMMemo.GetVisibleRows: Integer;
|
||||
begin
|
||||
Result := Height div fItemHeight;
|
||||
end;
|
||||
|
||||
|
||||
//fItems.Count or Height has changed
|
||||
procedure TKMMemo.UpdateScrollBar;
|
||||
var
|
||||
oldMax: Integer;
|
||||
begin
|
||||
oldMax := fScrollBar.MaxValue;
|
||||
fScrollBar.MaxValue := fItems.Count - GetVisibleRows;
|
||||
fScrollBar.Visible := IsSetVisible and (fScrollBar.MaxValue <> fScrollBar.MinValue);
|
||||
|
||||
if fScrollDown then
|
||||
begin
|
||||
if oldMax - fScrollBar.Position <= 2 then //If they were near the bottom BEFORE updating, keep them at the bottom
|
||||
SetTopIndex(fItems.Count) //This puts it at the bottom because of the EnsureRange in SetTopIndex
|
||||
end
|
||||
else
|
||||
SetTopIndex(fScrollBar.Position);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMMemo.SetCursorPos(aPos: Integer);
|
||||
begin
|
||||
fCursorPos := EnsureRange(aPos, 0, GetMaxCursorPos);
|
||||
end;
|
||||
|
||||
|
||||
function TKMMemo.GetPlainText(aUsePipeAsEOL: Boolean; aStart: Integer = -1; aEnd: Integer = -1): UnicodeString;
|
||||
begin
|
||||
if aStart = -1 then
|
||||
aStart := 0;
|
||||
|
||||
if aEnd = -1 then
|
||||
aEnd := Length(fItems.Text);
|
||||
|
||||
// First remove EOL's to get correct positions in text
|
||||
Result := StringReplace(fItems.Text, EolW, '|', [rfReplaceAll]);
|
||||
// Get text with selected positions, cleaned of color markup
|
||||
Result := Copy(GetNoColorMarkupText(Result), aStart + 1, aEnd - aStart);
|
||||
// Return EOL's back
|
||||
if not aUsePipeAsEOL then
|
||||
Result := StringReplace(Result, '|', EolW, [rfReplaceAll]);
|
||||
end;
|
||||
|
||||
|
||||
function TKMMemo.GetSelectedText: UnicodeString;
|
||||
begin
|
||||
Result := '';
|
||||
if HasSelection then
|
||||
Result := GetPlainText(False, fSelectionStart, fSelectionEnd);
|
||||
end;
|
||||
|
||||
|
||||
function TKMMemo.HasSelection: Boolean;
|
||||
begin
|
||||
Result := (fSelectionStart <> -1) and (fSelectionEnd <> -1) and (fSelectionStart <> fSelectionEnd);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMMemo.ResetSelection;
|
||||
begin
|
||||
fSelectionStart := -1;
|
||||
fSelectionEnd := -1;
|
||||
fSelectionInitialPos := -1;
|
||||
end;
|
||||
|
||||
|
||||
//We are using 2 systems for position: Linear (cumulative) and 'Point' (2D with column and row)
|
||||
//Every system have Length(RowText)+1 positions in every line
|
||||
//Convert Linear position into 2D position
|
||||
function TKMMemo.LinearToPointPos(aPos: Integer): TKMPoint;
|
||||
var I, row, column: Integer;
|
||||
rowText: UnicodeString;
|
||||
rowStartPos, rowEndPos: Integer;
|
||||
begin
|
||||
row := 0;
|
||||
column := 0;
|
||||
for I := 0 to fItems.Count - 1 do
|
||||
begin
|
||||
rowText := GetNoColorMarkupText(fItems[I]);
|
||||
rowStartPos := PointToLinearPos(0, I);
|
||||
rowEndPos := rowStartPos + Length(rowText);
|
||||
if InRange(aPos, rowStartPos, rowEndPos) then
|
||||
begin
|
||||
row := I;
|
||||
column := aPos - rowStartPos;
|
||||
Break;
|
||||
end;
|
||||
end;
|
||||
Result := TKMPoint.New(column, row);
|
||||
end;
|
||||
|
||||
|
||||
//We are using 2 systems for position: Linear (cumulative) and 'Point' (2D with column and row)
|
||||
//Every system have Length(RowText)+1 positions in every line
|
||||
//Convert 2D position into Linear position
|
||||
function TKMMemo.PointToLinearPos(aColumn, aRow: Integer): Integer;
|
||||
var
|
||||
I: Integer;
|
||||
begin
|
||||
Result := 0;
|
||||
aRow := EnsureRange(aRow, 0, fItems.Count-1);
|
||||
for I := 0 to aRow-1 do
|
||||
Inc(Result, Length(GetNoColorMarkupText(fItems[I])) + 1); //+1 for special KaM new line symbol ('|')
|
||||
Inc(Result, EnsureRange(aColumn, 0, GetMaxPosInRow(aRow)));
|
||||
end;
|
||||
|
||||
|
||||
//Return position of Cursor in 2D of point X,Y on the screen
|
||||
function TKMMemo.GetCharPosAt(X,Y: Integer): TKMPoint;
|
||||
var
|
||||
row, column: Integer;
|
||||
begin
|
||||
row := 0;
|
||||
column := 0;
|
||||
|
||||
if fItems.Count > 0 then
|
||||
begin
|
||||
row := (EnsureRange(Y-AbsTop-3, 0, Height-6) div fItemHeight) + TopIndex;
|
||||
row := EnsureRange(row, 0, fItems.Count-1);
|
||||
column := gRes.Fonts[fFont].CharsThatFit(GetNoColorMarkupText(fItems[row]), X-AbsLeft-4);
|
||||
end;
|
||||
Result := TKMPoint.New(column, row);
|
||||
end;
|
||||
|
||||
|
||||
//Return position of Cursor in linear system of point X,Y on the screen
|
||||
function TKMMemo.GetCursorPosAt(X,Y: Integer): Integer;
|
||||
var
|
||||
charPos: TKMPoint;
|
||||
begin
|
||||
charPos := GetCharPosAt(X, Y);
|
||||
Result := PointToLinearPos(charPos.X, charPos.Y);
|
||||
end;
|
||||
|
||||
|
||||
// Maximum possible position of Cursor
|
||||
function TKMMemo.GetMaxCursorPos: Integer;
|
||||
begin
|
||||
Result := Length(StringReplace(fItems.Text, EolW, '', [rfReplaceAll])) + EnsureRange(fItems.Count-1, 0, fItems.Count);
|
||||
end;
|
||||
|
||||
|
||||
// Minimum possible position of Cursor in its current Row
|
||||
function TKMMemo.GetMinCursorPosInRow: Integer;
|
||||
begin
|
||||
Result := GetMinPosInRow(LinearToPointPos(CursorPos).Y)
|
||||
end;
|
||||
|
||||
|
||||
// Maximum possible position of Cursor in its current Row
|
||||
function TKMMemo.GetMaxCursorPosInRow: Integer;
|
||||
begin
|
||||
Result := GetMaxPosInRow(LinearToPointPos(CursorPos).Y)
|
||||
end;
|
||||
|
||||
|
||||
// Minimum possible position of Cursor in the specified aRow
|
||||
function TKMMemo.GetMinPosInRow(aRow: Integer): Integer;
|
||||
begin
|
||||
if aRow = 0 then
|
||||
Result := 0
|
||||
else
|
||||
Result := GetMaxPosInRow(aRow - 1) + 1;
|
||||
end;
|
||||
|
||||
|
||||
// Maximum possible position of Cursor in the specified aRow
|
||||
function TKMMemo.GetMaxPosInRow(aRow: Integer): Integer;
|
||||
var
|
||||
I: Integer;
|
||||
begin
|
||||
Result := 0;
|
||||
for I := 0 to min(aRow, fItems.Count - 1) do
|
||||
Inc(Result, Length(GetNoColorMarkupText(fItems[I])) + 1);
|
||||
Result := EnsureRange(Result - 1, 0, Result);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMMemo.SetSelectionStart(aValue: Integer);
|
||||
var
|
||||
maxPos: Integer;
|
||||
begin
|
||||
if fSelectable then
|
||||
begin
|
||||
maxPos := GetMaxCursorPos;
|
||||
fSelectionStart := EnsureRange(aValue, 0, maxPos);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMMemo.SetSelectionEnd(aValue: Integer);
|
||||
var
|
||||
maxPos: Integer;
|
||||
begin
|
||||
if fSelectable then
|
||||
begin
|
||||
maxPos := GetMaxCursorPos;
|
||||
fSelectionEnd := EnsureRange(aValue, 0, maxPos);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
//Set selections with pair of value, using he fact, that fSelectionStart <= fSelectionEnd
|
||||
procedure TKMMemo.SetSelections(aValue1, aValue2: Integer);
|
||||
begin
|
||||
fSelectionStart := Min(aValue1, aValue2);
|
||||
fSelectionEnd := Max(aValue1, aValue2);
|
||||
end;
|
||||
|
||||
|
||||
//Update selection start/end due to change cursor position
|
||||
procedure TKMMemo.UpdateSelection(aOldCursorPos: Integer; aIsSelecting: Boolean);
|
||||
begin
|
||||
if aIsSelecting then
|
||||
begin
|
||||
if HasSelection then
|
||||
begin
|
||||
if aOldCursorPos = SelectionStart then
|
||||
SetSelections(CursorPos, SelectionEnd)
|
||||
else
|
||||
if aOldCursorPos = SelectionEnd then
|
||||
SetSelections(SelectionStart, CursorPos);
|
||||
end else
|
||||
SetSelections(aOldCursorPos, CursorPos);
|
||||
end
|
||||
else
|
||||
ResetSelection;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMMemo.Add(const aItem: UnicodeString);
|
||||
begin
|
||||
if fText <> '' then
|
||||
fText := fText + '|';
|
||||
|
||||
fText := fText + aItem; //Append the new string
|
||||
|
||||
SetText(fText); //Updates the text in fItems
|
||||
UpdateScrollBar; //Scroll down with each item that is added.
|
||||
|
||||
if Assigned(fOnChange) then
|
||||
fOnChange(Self);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMMemo.Clear;
|
||||
begin
|
||||
fText := '';
|
||||
fItems.Clear;
|
||||
ResetSelection;
|
||||
UpdateScrollBar;
|
||||
|
||||
if Assigned(fOnChange) then
|
||||
fOnChange(Self);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMMemo.ScrollToBottom;
|
||||
begin
|
||||
SetTopIndex(fItems.Count);
|
||||
end;
|
||||
|
||||
|
||||
//Key events which have no effect should not be handled (allows scrolling while chat window open with no text entered)
|
||||
function TKMMemo.KeyEventHandled(Key: Word; Shift: TShiftState): Boolean;
|
||||
begin
|
||||
Result := True;
|
||||
|
||||
//Don't include backspace/delete because edits should always handle those. Otherwise when you
|
||||
//press backspace repeatedly to remove all characters it will apply other shortcuts like
|
||||
//resetting the zoom if you press it once too many times.
|
||||
case Key of
|
||||
VK_UP,
|
||||
VK_DOWN,
|
||||
VK_LEFT,
|
||||
VK_RIGHT,
|
||||
VK_HOME,
|
||||
VK_END,
|
||||
VK_PRIOR,
|
||||
VK_NEXT: Result := (fText <> ''); //These keys have no effect when text is blank
|
||||
end;
|
||||
|
||||
//We want these keys to be ignored by chat, so game shortcuts still work
|
||||
if Key in [VK_F1..VK_F12, VK_ESCAPE, VK_MBUTTON] then Result := False;
|
||||
|
||||
//Ctrl can be used as an escape character, e.g. CTRL+B places beacon while chat is open
|
||||
if ssCtrl in Shift then Result := (Key in [VK_LEFT, VK_RIGHT, Ord('A'), Ord('C'), Ord('X')]);
|
||||
end;
|
||||
|
||||
|
||||
function TKMMemo.KeyDown(Key: Word; Shift: TShiftState): Boolean;
|
||||
//Move cursor vertically (change cursor row)
|
||||
procedure MoveCursorVertically(aRowIncrement: Integer);
|
||||
var
|
||||
cursorPointPos: TKMPoint;
|
||||
newCursorPosY: Integer;
|
||||
srcLineText, destLineText: UnicodeString;
|
||||
begin
|
||||
cursorPointPos := LinearToPointPos(CursorPos);
|
||||
newCursorPosY := EnsureRange(cursorPointPos.Y + aRowIncrement, 0, fItems.Count-1);
|
||||
if newCursorPosY <> cursorPointPos.Y then
|
||||
begin
|
||||
// Because we don't use monospaces fonts, then its better to find proper column, which depends of text width in px
|
||||
srcLineText := GetNoColorMarkupText(Copy(fItems[cursorPointPos.Y], 1, cursorPointPos.X));
|
||||
destLineText := GetNoColorMarkupText(fItems[newCursorPosY]);
|
||||
//Use 'rounding' version of CharsThatFit to get more precise position
|
||||
cursorPointPos.X := gRes.Fonts[fFont].CharsThatFit(destLineText, gRes.Fonts[fFont].GetTextSize(srcLineText).X, True);
|
||||
CursorPos := PointToLinearPos(cursorPointPos.X, newCursorPosY);
|
||||
// Update scroll position, if needed
|
||||
if TopIndex > newCursorPosY then
|
||||
TopIndex := newCursorPosY
|
||||
else if TopIndex < newCursorPosY - GetVisibleRows + 1 then
|
||||
TopIndex := newCursorPosY - GetVisibleRows + 1;
|
||||
end;
|
||||
end;
|
||||
|
||||
// Update cursor pos considering Ctrl Key to go to the next / prev word in the text
|
||||
procedure UpdateCursorPos(aCursorDir: TKMCursorDir);
|
||||
begin
|
||||
if ssCtrl in Shift then
|
||||
begin
|
||||
case aCursorDir of
|
||||
cdNone: ;
|
||||
cdForward: CursorPos := GetNextWordPos(GetPlainText(True), CursorPos);
|
||||
cdBack: CursorPos := GetPrevWordPos(GetPlainText(True), CursorPos);
|
||||
end;
|
||||
end
|
||||
else
|
||||
CursorPos := CursorPos + ShortInt(aCursorDir);
|
||||
end;
|
||||
|
||||
var
|
||||
oldCursorPos: Integer;
|
||||
isSelect: Boolean;
|
||||
begin
|
||||
Result := KeyEventHandled(Key, Shift);
|
||||
if inherited KeyDown(Key, Shift) then Exit;
|
||||
|
||||
//Clipboard operations
|
||||
if (Shift = [ssCtrl]) and (Key <> VK_CONTROL) then
|
||||
case Key of
|
||||
Ord('A'): SetSelections(0, GetMaxCursorPos);
|
||||
Ord('C'),
|
||||
Ord('X'): if HasSelection then
|
||||
Clipboard.AsText := GetSelectedText;
|
||||
end;
|
||||
|
||||
oldCursorPos := CursorPos;
|
||||
isSelect := IsSelecting(Key, Shift);
|
||||
|
||||
case Key of
|
||||
VK_LEFT,
|
||||
VK_RIGHT: begin
|
||||
UpdateCursorPos(GetCursorDir(Key));
|
||||
UpdateSelection(oldCursorPos, isSelect);
|
||||
end;
|
||||
VK_UP: begin
|
||||
MoveCursorVertically(-1);
|
||||
UpdateSelection(oldCursorPos, isSelect);
|
||||
end;
|
||||
VK_DOWN: begin
|
||||
MoveCursorVertically(1);
|
||||
UpdateSelection(oldCursorPos, isSelect);
|
||||
end;
|
||||
VK_PRIOR: begin
|
||||
MoveCursorVertically(-GetVisibleRows);
|
||||
UpdateSelection(oldCursorPos, isSelect);
|
||||
end;
|
||||
VK_NEXT: begin
|
||||
MoveCursorVertically(GetVisibleRows);
|
||||
UpdateSelection(oldCursorPos, isSelect);
|
||||
end;
|
||||
VK_HOME: begin
|
||||
CursorPos := GetMinCursorPosInRow;
|
||||
UpdateSelection(oldCursorPos, isSelect);
|
||||
end;
|
||||
VK_END: begin
|
||||
CursorPos := GetMaxCursorPosInRow;
|
||||
UpdateSelection(oldCursorPos, isSelect);
|
||||
end;
|
||||
end
|
||||
end;
|
||||
|
||||
|
||||
function TKMMemo.KeyUp(Key: Word; Shift: TShiftState): Boolean;
|
||||
begin
|
||||
Result := inherited KeyUp(Key, Shift) or KeyEventHandled(Key, Shift);
|
||||
end;
|
||||
|
||||
|
||||
function TKMMemo.DoHandleMouseWheelByDefault: Boolean;
|
||||
begin
|
||||
Result := False;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMMemo.MouseWheel(Sender: TObject; WheelSteps: Integer; var aHandled: Boolean);
|
||||
begin
|
||||
inherited;
|
||||
|
||||
if aHandled then Exit;
|
||||
|
||||
SetTopIndex(TopIndex - WheelSteps);
|
||||
|
||||
aHandled := WheelSteps <> 0;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMMemo.MouseDown(X,Y: Integer; Shift: TShiftState; Button: TMouseButton);
|
||||
var
|
||||
oldCursorPos: Integer;
|
||||
begin
|
||||
inherited;
|
||||
|
||||
Focusable := fSelectable and (fText <> ''); // Do not focus on empty Memo's
|
||||
// Update Focus now, because we need to focus on MouseDown, not on MouseUp as by default for all controls
|
||||
MasterParent.MasterControl.UpdateFocus(Self);
|
||||
|
||||
oldCursorPos := CursorPos;
|
||||
CursorPos := GetCursorPosAt(X, Y);
|
||||
|
||||
if Focusable then
|
||||
begin
|
||||
//Try select on Shift + LMB click
|
||||
UpdateSelection(oldCursorPos, (oldCursorPos <> -1) and (Shift = [ssLeft, ssShift]));
|
||||
fSelectionInitialPos := CursorPos;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMMemo.MouseMove(X,Y: Integer; Shift: TShiftState);
|
||||
var
|
||||
oldCursorPos: Integer;
|
||||
charPos: TKMPoint;
|
||||
begin
|
||||
inherited;
|
||||
if (ssLeft in Shift) and (fSelectionInitialPos <> -1) then
|
||||
begin
|
||||
charPos := GetCharPosAt(X, Y);
|
||||
|
||||
// To rotate text to top while selecting
|
||||
if (Y-AbsTop-3 < 0) and (TopIndex > 0) then
|
||||
begin
|
||||
Dec(charPos.Y);
|
||||
SetTopIndex(TopIndex-1);
|
||||
end;
|
||||
// To rotate text to bottom while selecting
|
||||
if (Y-AbsTop-3 > Height)
|
||||
and (fScrollBar.Position < fScrollBar.MaxValue) then
|
||||
begin
|
||||
charPos.Y := EnsureRange(charPos.Y+1, 0, fItems.Count);
|
||||
SetTopIndex(TopIndex+1);
|
||||
end;
|
||||
|
||||
oldCursorPos := CursorPos;
|
||||
CursorPos := PointToLinearPos(charPos.X, charPos.Y);;
|
||||
UpdateSelection(oldCursorPos, True);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMMemo.MouseUp(X,Y: Integer; Shift: TShiftState; Button: TMouseButton);
|
||||
begin
|
||||
inherited;
|
||||
fSelectionInitialPos := -1;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMMemo.ControlMouseDown(Sender: TObject; X,Y: Integer; Shift: TShiftState; Button: TMouseButton);
|
||||
begin
|
||||
// Reset all, if other control was clicked
|
||||
if Sender <> Self then
|
||||
begin
|
||||
ResetSelection;
|
||||
Focusable := False;
|
||||
CursorPos := -1;
|
||||
MasterParent.MasterControl.UpdateFocus(Self);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMMemo.FocusChanged(aFocused: Boolean);
|
||||
begin
|
||||
if not aFocused then
|
||||
begin
|
||||
ResetSelection;
|
||||
CursorPos := -1;
|
||||
Focusable := False;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMMemo.Paint;
|
||||
var
|
||||
I, paintWidth, selPaintTop, selPaintHeight: Integer;
|
||||
beforeSelectionText, selectionText, rowText: UnicodeString;
|
||||
beforeSelectionW, selectionW, selStartPosInRow, selEndPosInRow, rowStartPos, rowEndPos: Integer;
|
||||
offX, offY: Integer;
|
||||
begin
|
||||
inherited;
|
||||
if fScrollBar.Visible then
|
||||
paintWidth := Width - fScrollBar.Width //Leave space for scrollbar
|
||||
else
|
||||
paintWidth := Width; //List takes up the entire width
|
||||
|
||||
TKMRenderUI.WriteBevel(AbsLeft, AbsTop, paintWidth, Height, 1, 0.5);
|
||||
|
||||
for I := 0 to Math.min(fItems.Count-1, GetVisibleRows - 1) do
|
||||
begin
|
||||
rowText := GetNoColorMarkupText(fItems[TopIndex+I]);
|
||||
rowStartPos := PointToLinearPos(0, TopIndex+I);
|
||||
rowEndPos := rowStartPos + Length(rowText);
|
||||
if HasSelection then
|
||||
begin
|
||||
selStartPosInRow := fSelectionStart - rowStartPos;
|
||||
selEndPosInRow := fSelectionEnd - rowStartPos;
|
||||
|
||||
selStartPosInRow := EnsureRange(selStartPosInRow, 0, rowEndPos);
|
||||
selEndPosInRow := EnsureRange(selEndPosInRow, selStartPosInRow, rowEndPos);
|
||||
|
||||
if selStartPosInRow <> selEndPosInRow then
|
||||
begin
|
||||
beforeSelectionText := Copy(rowText, 1, selStartPosInRow);
|
||||
selectionText := Copy(rowText, selStartPosInRow+1, selEndPosInRow - selStartPosInRow);
|
||||
|
||||
beforeSelectionW := gRes.Fonts[fFont].GetTextSize(beforeSelectionText).X;
|
||||
selectionW := gRes.Fonts[fFont].GetTextSize(selectionText).X;
|
||||
|
||||
selPaintHeight := fItemHeight;
|
||||
selPaintTop := AbsTop+I*fItemHeight;
|
||||
if I = 0 then
|
||||
begin
|
||||
Dec(selPaintHeight, 3);
|
||||
Inc(selPaintTop, 3);
|
||||
end;
|
||||
|
||||
TKMRenderUI.WriteShape(AbsLeft+4+beforeSelectionW, selPaintTop, min(selectionW, Width-8), selPaintHeight, clTextSelection);
|
||||
end;
|
||||
end;
|
||||
|
||||
//Render text cursor
|
||||
if fSelectable and (csFocus in State) and ((TimeGet div 500) mod 2 = 0)
|
||||
and InRange(CursorPos, rowStartPos, rowEndPos) then
|
||||
begin
|
||||
offX := AbsLeft + 2 + gRes.Fonts[fFont].GetTextSize(Copy(rowText, 1, CursorPos-rowStartPos)).X;
|
||||
offY := AbsTop + 2 + I*fItemHeight;
|
||||
TKMRenderUI.WriteShape(offX, offY, 3, fItemHeight-4, $FFFFFFFF, $FF000000);
|
||||
end;
|
||||
TKMRenderUI.WriteText(AbsLeft+4, AbsTop+I*fItemHeight+3, Width-8, fItems.Strings[TopIndex+I] , fFont, taLeft);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
end.
|
||||
|
||||
@@ -0,0 +1,339 @@
|
||||
unit KM_ControlsMinimapView;
|
||||
{$I KaM_Remake.inc}
|
||||
interface
|
||||
uses
|
||||
Classes, Controls,
|
||||
KromOGLUtils,
|
||||
KM_RenderUI, KM_Minimap, KM_Viewport,
|
||||
KM_ResFonts, KM_CommonTypes, KM_Points, KM_Defaults,
|
||||
KM_Controls, KM_ControlsBase;
|
||||
|
||||
|
||||
type
|
||||
TKMMinimapEvent = procedure (Sender: TObject; const aLocX, aLocY: Integer) of object;
|
||||
TKMMinimapLocEvent = procedure (Sender: TObject; const aLoc: Integer) of object;
|
||||
|
||||
// MinimapView relies on fMinimap and fViewport that provide all the data
|
||||
// MinimapView itself is just a painter
|
||||
TKMMinimapView = class(TKMControl)
|
||||
private
|
||||
fBevel: TKMBevel;
|
||||
fMinimap: TKMMinimap;
|
||||
fView: TKMViewport;
|
||||
fPaintWidth: Integer;
|
||||
fPaintHeight: Integer;
|
||||
fLeftOffset: Integer;
|
||||
fTopOffset: Integer;
|
||||
|
||||
fMapTex: TTexture;
|
||||
fWidthPOT: Word;
|
||||
fHeightPOT: Word;
|
||||
|
||||
fOnMinimapChange: TKMMinimapEvent;
|
||||
fOnMinimapClick: TKMMinimapEvent;
|
||||
fShowLocs: Boolean;
|
||||
fLocRad: Byte;
|
||||
fClickableOnce: Boolean;
|
||||
procedure UpdateTexture;
|
||||
procedure ResizeMinimap;
|
||||
protected
|
||||
procedure SetAnchors(aValue: TKMAnchorsSet); override;
|
||||
public
|
||||
OnMinimapLocationClick: TKMMinimapLocEvent;
|
||||
|
||||
constructor Create(aMinimap: TKMMinimap; aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; aWithBevel: Boolean = False);
|
||||
|
||||
function LocalToMapCoords(X,Y: Integer): TKMPoint;
|
||||
function MapCoordsToLocal(X,Y: Single; const Inset: ShortInt = 0): TKMPoint;
|
||||
procedure UpdateSizes;
|
||||
procedure SetViewport(aViewport: TKMViewport);
|
||||
property ShowLocs: Boolean read fShowLocs write fShowLocs;
|
||||
property ClickableOnce: Boolean read fClickableOnce write fClickableOnce;
|
||||
property OnMinimapChange: TKMMinimapEvent write fOnMinimapChange;
|
||||
property OnMinimapClick: TKMMinimapEvent read fOnMinimapClick write fOnMinimapClick;
|
||||
|
||||
procedure MouseDown(X,Y: Integer; Shift: TShiftState; Button: TMouseButton); override;
|
||||
procedure MouseUp(X,Y: Integer; Shift: TShiftState; Button: TMouseButton); override;
|
||||
procedure MouseMove(X,Y: Integer; Shift: TShiftState); override;
|
||||
procedure Paint; override;
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
uses
|
||||
SysUtils,
|
||||
Math,
|
||||
KromUtils,
|
||||
KM_MinimapGame,
|
||||
KM_Render, KM_RenderTypes,
|
||||
KM_CommonUtils,
|
||||
KM_GameSettings, KM_InterfaceTypes;
|
||||
|
||||
|
||||
{ TKMMinimapView }
|
||||
constructor TKMMinimapView.Create(aMinimap: TKMMinimap; aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; aWithBevel: Boolean = False);
|
||||
begin
|
||||
//Create Bevel first
|
||||
if aWithBevel then
|
||||
fBevel := TKMBevel.Create(aParent, aLeft - 4, aTop - 4, aWidth + 8, aHeight + 8);
|
||||
|
||||
//Then Minimap control itself
|
||||
inherited Create(aParent, aLeft, aTop, aWidth, aHeight);
|
||||
|
||||
//Radius of circle around player location
|
||||
fLocRad := 8;
|
||||
|
||||
fMinimap := aMinimap;
|
||||
if fMinimap <> nil then
|
||||
begin
|
||||
fMinimap.SubOnUpdateTexture(UpdateTexture);
|
||||
fMinimap.SubOnResize(ResizeMinimap);
|
||||
|
||||
fMapTex.Tex := TKMRender.GenerateTextureCommon(ftNearest, ftNearest);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMMinimapView.UpdateSizes;
|
||||
begin
|
||||
if fMinimap.MapX > fMinimap.MapY then
|
||||
begin
|
||||
fPaintWidth := Width;
|
||||
fPaintHeight := Round(Height * fMinimap.MapY / Max(fMinimap.MapX, 1)); // X could = 0
|
||||
fLeftOffset := 0;
|
||||
fTopOffset := (Height - fPaintHeight) div 2;
|
||||
end
|
||||
else
|
||||
begin
|
||||
fPaintWidth := Round(Width * fMinimap.MapX / Max(fMinimap.MapY, 1)); // Y could = 0
|
||||
fPaintHeight := Height;
|
||||
fLeftOffset := (Width - fPaintWidth) div 2;
|
||||
fTopOffset := 0;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMMinimapView.SetViewport(aViewport: TKMViewport);
|
||||
begin
|
||||
fView := aViewport;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMMinimapView.ResizeMinimap;
|
||||
begin
|
||||
if Self = nil then Exit;
|
||||
|
||||
fWidthPOT := MakePOT(fMinimap.MapX);
|
||||
fHeightPOT := MakePOT(fMinimap.MapY);
|
||||
fMapTex.U := fMinimap.MapX / fWidthPOT;
|
||||
fMapTex.V := fMinimap.MapY / fHeightPOT;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMMinimapView.UpdateTexture;
|
||||
var
|
||||
wData: Pointer;
|
||||
I: Word;
|
||||
begin
|
||||
if Self = nil then Exit;
|
||||
|
||||
GetMem(wData, fWidthPOT * fHeightPOT * 4);
|
||||
|
||||
if fMinimap.MapY > 0 then //if MapY = 0 then loop will overflow to MaxWord
|
||||
for I := 0 to fMinimap.MapY - 1 do
|
||||
Move(Pointer(NativeUint(fMinimap.Base) + I * fMinimap.MapX * 4)^,
|
||||
Pointer(NativeUint(wData) + I * fWidthPOT * 4)^, fMinimap.MapX * 4);
|
||||
|
||||
TKMRender.UpdateTexture(fMapTex.Tex, fWidthPOT, fHeightPOT, tfRGBA8, wData);
|
||||
FreeMem(wData);
|
||||
|
||||
UpdateSizes;
|
||||
end;
|
||||
|
||||
|
||||
function TKMMinimapView.LocalToMapCoords(X,Y: Integer): TKMPoint;
|
||||
begin
|
||||
Result.X := EnsureRange(Trunc((X - AbsLeft - fLeftOffset) * (fMinimap.MapX + 1) / fPaintWidth), 1, fMinimap.MapX);
|
||||
Result.Y := EnsureRange(Trunc((Y - AbsTop - fTopOffset ) * (fMinimap.MapY + 1) / fPaintHeight), 1, fMinimap.MapY);
|
||||
end;
|
||||
|
||||
|
||||
function TKMMinimapView.MapCoordsToLocal(X,Y: Single; const Inset: ShortInt = 0): TKMPoint;
|
||||
begin
|
||||
Assert(Inset >= -1, 'Min allowed inset is -1, to be within TKMPoint range of 0..n');
|
||||
Result.X := AbsLeft + fLeftOffset + EnsureRange(Round(X * fPaintWidth / fMinimap.MapX), Inset, fPaintWidth - Inset);
|
||||
Result.Y := AbsTop + fTopOffset + EnsureRange(Round(Y * fPaintHeight / fMinimap.MapY), Inset, fPaintHeight - Inset);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMMinimapView.SetAnchors(aValue: TKMAnchorsSet);
|
||||
begin
|
||||
inherited;
|
||||
|
||||
if fBevel <> nil then
|
||||
fBevel.Anchors := aValue;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMMinimapView.MouseDown(X,Y: Integer; Shift: TShiftState; Button: TMouseButton);
|
||||
begin
|
||||
inherited;
|
||||
|
||||
MouseMove(X,Y,Shift);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMMinimapView.MouseMove(X,Y: Integer; Shift: TShiftState);
|
||||
var
|
||||
viewPos: TKMPoint;
|
||||
begin
|
||||
inherited;
|
||||
|
||||
if (ssLeft in Shift) and not fClickableOnce then
|
||||
begin
|
||||
viewPos := LocalToMapCoords(X,Y);
|
||||
if Assigned(fOnMinimapChange) then
|
||||
fOnMinimapChange(Self, viewPos.X, viewPos.Y);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMMinimapView.MouseUp(X,Y: Integer; Shift: TShiftState; Button: TMouseButton);
|
||||
var
|
||||
I: Integer;
|
||||
T, viewPos: TKMPoint;
|
||||
begin
|
||||
inherited;
|
||||
|
||||
if fClickableOnce then
|
||||
begin
|
||||
fClickableOnce := False; //Not clickable anymore
|
||||
viewPos := LocalToMapCoords(X,Y);
|
||||
if Assigned(fOnMinimapClick) then
|
||||
fOnMinimapClick(Self, viewPos.X, viewPos.Y);
|
||||
end;
|
||||
|
||||
if fShowLocs then
|
||||
for I := 0 to MAX_HANDS - 1 do
|
||||
if fMinimap.HandShow[I] and not KMSamePoint(fMinimap.HandLocs[I], KMPOINT_ZERO) then
|
||||
begin
|
||||
T := MapCoordsToLocal(fMinimap.HandLocs[I].X, fMinimap.HandLocs[I].Y, fLocRad);
|
||||
if Sqr(T.X - X) + Sqr(T.Y - Y) < Sqr(fLocRad) then
|
||||
begin
|
||||
if Assigned(OnMinimapLocationClick) then
|
||||
OnMinimapLocationClick(Self, I);
|
||||
|
||||
// Do not repeat events for overlapping locations
|
||||
Break;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMMinimapView.Paint;
|
||||
const
|
||||
ALERT_RAD = 4;
|
||||
var
|
||||
I, K: Integer;
|
||||
R: TKMRect;
|
||||
S: TKMDirection4Set;
|
||||
T, T1, T2: TKMPoint;
|
||||
minimapGame: TKMMiniMapGame;
|
||||
miniLeft, miniTop, miniRight, miniBottom: SmallInt;
|
||||
begin
|
||||
inherited;
|
||||
|
||||
if (fMinimap = nil) or (fMinimap.MapX * fMinimap.MapY = 0) then
|
||||
Exit;
|
||||
|
||||
if (fMapTex.Tex <> 0) then
|
||||
TKMRenderUI.WriteTexture(AbsLeft + fLeftOffset, AbsTop + fTopOffset, fPaintWidth, fPaintHeight, fMapTex, $FFFFFFFF)
|
||||
else
|
||||
TKMRenderUI.WriteBevel(AbsLeft, AbsTop, Width, Height);
|
||||
|
||||
//Alerts (under viewport rectangle)
|
||||
if fMinimap is TKMMiniMapGame then
|
||||
begin
|
||||
minimapGame := TKMMiniMapGame(fMinimap);
|
||||
|
||||
if (minimapGame.Alerts <> nil) then
|
||||
for I := 0 to minimapGame.Alerts.Count - 1 do
|
||||
if minimapGame.Alerts[I].VisibleMinimap then
|
||||
begin
|
||||
T := MapCoordsToLocal(minimapGame.Alerts[I].Loc.X, minimapGame.Alerts[I].Loc.Y, ALERT_RAD);
|
||||
TKMRenderUI.WritePicture(T.X, T.Y, 0, 0, [],
|
||||
minimapGame.Alerts[I].TexMinimap.RX, minimapGame.Alerts[I].TexMinimap.ID,
|
||||
True, minimapGame.Alerts[I].TeamColor, Abs((TimeGet mod 1000) / 500 - 1));
|
||||
end;
|
||||
|
||||
//Viewport rectangle
|
||||
if fView <> nil then
|
||||
begin
|
||||
R := fView.GetMinimapClip;
|
||||
if (R.Right - R.Left) * (R.Bottom - R.Top) > 0 then
|
||||
begin
|
||||
if gGameSettings.ZoomBehaviour = zbRestricted then
|
||||
begin
|
||||
TKMRenderUI.WriteOutline(AbsLeft + fLeftOffset + Round((R.Left - 1)*fPaintWidth / fMinimap.MapX),
|
||||
AbsTop + fTopOffset + Round((R.Top - 1)*fPaintHeight / fMinimap.MapY),
|
||||
Round((R.Right - R.Left)*fPaintWidth / fMinimap.MapX),
|
||||
Round((R.Bottom - R.Top)*fPaintHeight / fMinimap.MapY), 1, $FFFFFFFF);
|
||||
end
|
||||
else
|
||||
begin
|
||||
miniLeft := AbsLeft + fLeftOffset + Round((R.Left - 1)*fPaintWidth / fMinimap.MapX) + 1;
|
||||
miniTop := AbsTop + fTopOffset + Round((R.Top - 1)*fPaintHeight / fMinimap.MapY) + 1;
|
||||
miniRight := AbsLeft + fLeftOffset + Round((R.Right - 1)*fPaintWidth / fMinimap.MapX);
|
||||
miniBottom := AbsTop + fTopOffset + Round((R.Bottom - 1)*fPaintHeight / fMinimap.MapY);
|
||||
|
||||
S := fView.GetMinimapClipLines;
|
||||
if drW in S then
|
||||
TKMRenderUI.WriteLine(miniLeft, miniBottom, miniLeft, miniTop, $FFFFFFFF);
|
||||
if drN in S then
|
||||
TKMRenderUI.WriteLine(miniLeft-1, miniTop, miniRight, miniTop, $FFFFFFFF);
|
||||
if drE in S then
|
||||
TKMRenderUI.WriteLine(miniRight, miniBottom, miniRight, miniTop, $FFFFFFFF);
|
||||
if drS in S then
|
||||
TKMRenderUI.WriteLine(miniLeft-1, miniBottom, miniRight, miniBottom, $FFFFFFFF);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
if fShowLocs then
|
||||
begin
|
||||
//Connect allied players
|
||||
for I := 0 to MAX_HANDS - 1 do
|
||||
if fMinimap.HandShow[I] and not KMSamePoint(fMinimap.HandLocs[I], KMPOINT_ZERO) then
|
||||
for K := I + 1 to MAX_HANDS - 1 do
|
||||
if fMinimap.HandShow[K] and not KMSamePoint(fMinimap.HandLocs[K], KMPOINT_ZERO) then
|
||||
if (fMinimap.HandTeam[I] <> 0) and (fMinimap.HandTeam[I] = fMinimap.HandTeam[K]) then
|
||||
begin
|
||||
T1 := MapCoordsToLocal(fMinimap.HandLocs[I].X, fMinimap.HandLocs[I].Y, fLocRad);
|
||||
T2 := MapCoordsToLocal(fMinimap.HandLocs[K].X, fMinimap.HandLocs[K].Y, fLocRad);
|
||||
TKMRenderUI.WriteLine(T1.X, T1.Y, T2.X, T2.Y, $FFFFFFFF);
|
||||
end;
|
||||
|
||||
//Draw all the circles, THEN all the numbers so the numbers are not covered by circles when they are close
|
||||
for I := 0 to MAX_HANDS - 1 do
|
||||
if fMinimap.HandShow[I] and not KMSamePoint(fMinimap.HandLocs[I], KMPOINT_ZERO) then
|
||||
begin
|
||||
T := MapCoordsToLocal(fMinimap.HandLocs[I].X, fMinimap.HandLocs[I].Y, fLocRad);
|
||||
TKMRenderUI.WriteCircle(T.X, T.Y, fLocRad, fMinimap.HandColors[I]);
|
||||
end;
|
||||
|
||||
for I := 0 to MAX_HANDS - 1 do
|
||||
if fMinimap.HandShow[I] and not KMSamePoint(fMinimap.HandLocs[I], KMPOINT_ZERO) then
|
||||
begin
|
||||
T := MapCoordsToLocal(fMinimap.HandLocs[I].X, fMinimap.HandLocs[I].Y, fLocRad);
|
||||
TKMRenderUI.WriteText(T.X, T.Y - 6, 0, IntToStr(I+1), fntOutline, taCenter);
|
||||
end;
|
||||
end;
|
||||
|
||||
if not Enabled then
|
||||
TKMRenderUI.WriteBevel(AbsLeft, AbsTop, Width+1, Height+1, 0, 0.5);
|
||||
end;
|
||||
|
||||
|
||||
end.
|
||||
|
||||
+176
@@ -0,0 +1,176 @@
|
||||
unit KM_ControlsPopUp;
|
||||
{$I KaM_Remake.inc}
|
||||
interface
|
||||
uses
|
||||
Classes, Controls,
|
||||
KM_Controls, KM_ControlsBase, KM_ControlsList,
|
||||
KM_CommonTypes, KM_Points,
|
||||
KM_ResFonts;
|
||||
|
||||
|
||||
type
|
||||
TKMPopUpMenuMode = (
|
||||
pmActOnMouseUp, // Action triggered only by mouse up (default)
|
||||
pmActOnMouseDownNMove // Action triggered by mouse down and mouse move (drag)
|
||||
);
|
||||
|
||||
type
|
||||
// Popup with a short list (context menu)
|
||||
// For bigger taks - use TKMForm!
|
||||
TKMPopUpMenu = class(TKMPanel)
|
||||
private
|
||||
fMenuMode: TKMPopUpMenuMode;
|
||||
procedure MenuHide(Sender: TObject);
|
||||
procedure MenuActionTriggered(Sender: TObject);
|
||||
procedure HandleOtherControlMouseUp(Sender: TObject; X,Y: Integer; Shift: TShiftState; Button: TMouseButton);
|
||||
procedure SetItemIndex(aValue: Integer);
|
||||
function GetItemIndex: Integer;
|
||||
function GetItemTag(aIndex: Integer): Integer;
|
||||
protected
|
||||
Shape_Background: TKMShape;
|
||||
ColumnBox_List: TKMColumnBox;
|
||||
public
|
||||
constructor Create(aParent: TKMPanel; aWidth: Integer; aMenuMode: TKMPopUpMenuMode = pmActOnMouseUp);
|
||||
procedure AddItem(const aCaption: UnicodeString; aTag: Integer = 0);
|
||||
procedure UpdateItem(aIndex: Integer; const aCaption: UnicodeString);
|
||||
procedure Clear;
|
||||
property ItemIndex: Integer read GetItemIndex write SetItemIndex;
|
||||
property ItemTags[aIndex: Integer]: Integer read GetItemTag;
|
||||
procedure ShowAt(X,Y: Integer);
|
||||
procedure HideMenu;
|
||||
|
||||
property MenuMode: TKMPopUpMenuMode read fMenuMode;
|
||||
|
||||
procedure MouseUp(X,Y: Integer; Shift: TShiftState; Button: TMouseButton); override;
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
uses
|
||||
Math,
|
||||
KM_RenderUI;
|
||||
|
||||
|
||||
{ TKMPopUpMenu }
|
||||
constructor TKMPopUpMenu.Create(aParent: TKMPanel; aWidth: Integer; aMenuMode: TKMPopUpMenuMode = pmActOnMouseUp);
|
||||
begin
|
||||
inherited Create(aParent, 0, 0, aWidth, 0);
|
||||
|
||||
fMenuMode := aMenuMode;
|
||||
|
||||
// Note that Shape belongs to us, but we will manually position it to align with Parent to cover it
|
||||
Shape_Background := TKMShape.Create(Self, 0, 0, aParent.Width, aParent.Height);
|
||||
Shape_Background.OnClick := MenuHide;
|
||||
|
||||
ColumnBox_List := TKMColumnBox.Create(Self, 0, 0, aWidth, 0, fntGrey, bsMenu);
|
||||
ColumnBox_List.AnchorsStretch;
|
||||
ColumnBox_List.BackAlpha := 0.8;
|
||||
ColumnBox_List.Focusable := False;
|
||||
ColumnBox_List.SetColumns(fntGrey, [''], [0]);
|
||||
ColumnBox_List.ShowHeader := False;
|
||||
|
||||
case fMenuMode of
|
||||
pmActOnMouseUp: ColumnBox_List.OnClick := MenuActionTriggered;
|
||||
pmActOnMouseDownNMove: ColumnBox_List.OnChange := MenuActionTriggered;
|
||||
end;
|
||||
|
||||
fMasterControl.SubscribeOnOtherMouseUp(HandleOtherControlMouseUp);
|
||||
|
||||
Hide;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMPopUpMenu.Clear;
|
||||
begin
|
||||
ColumnBox_List.Clear;
|
||||
end;
|
||||
|
||||
|
||||
function TKMPopUpMenu.GetItemIndex: Integer;
|
||||
begin
|
||||
Result := ColumnBox_List.ItemIndex;
|
||||
end;
|
||||
|
||||
|
||||
function TKMPopUpMenu.GetItemTag(aIndex: Integer): Integer;
|
||||
begin
|
||||
Result := ColumnBox_List.Rows[aIndex].Tag;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMPopUpMenu.SetItemIndex(aValue: Integer);
|
||||
begin
|
||||
ColumnBox_List.ItemIndex := aValue;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMPopUpMenu.AddItem(const aCaption: UnicodeString; aTag: Integer = 0);
|
||||
begin
|
||||
ColumnBox_List.AddItem(MakeListRow([aCaption], aTag));
|
||||
|
||||
// Set own height (and anchored List will follow)
|
||||
Height := ColumnBox_List.ItemHeight * ColumnBox_List.RowCount;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMPopUpMenu.UpdateItem(aIndex: Integer; const aCaption: UnicodeString);
|
||||
begin
|
||||
ColumnBox_List.Rows[aIndex].Cells[0].Caption := aCaption;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMPopUpMenu.MenuActionTriggered(Sender: TObject);
|
||||
begin
|
||||
if Assigned(OnClick) then
|
||||
OnClick(Self);
|
||||
|
||||
if fMenuMode = pmActOnMouseUp then
|
||||
HideMenu;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMPopUpMenu.MenuHide(Sender: TObject);
|
||||
begin
|
||||
Hide;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMPopUpMenu.HideMenu;
|
||||
begin
|
||||
MenuHide(nil);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMPopUpMenu.ShowAt(X, Y: Integer);
|
||||
begin
|
||||
// Position self (and List will follow)
|
||||
AbsLeft := X;
|
||||
AbsTop := Y;
|
||||
|
||||
// Actualize modal background position
|
||||
Shape_Background.AbsLeft := Parent.AbsLeft;
|
||||
Shape_Background.AbsTop := Parent.AbsTop;
|
||||
|
||||
// Reset previously selected item
|
||||
ColumnBox_List.ItemIndex := -1;
|
||||
|
||||
Show;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMPopUpMenu.HandleOtherControlMouseUp(Sender: TObject; X,Y: Integer; Shift: TShiftState; Button: TMouseButton);
|
||||
begin
|
||||
MouseUp(X, Y, Shift, Button);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMPopUpMenu.MouseUp(X, Y: Integer; Shift: TShiftState; Button: TMouseButton);
|
||||
begin
|
||||
inherited;
|
||||
|
||||
Hide;
|
||||
end;
|
||||
|
||||
|
||||
end.
|
||||
|
||||
@@ -0,0 +1,350 @@
|
||||
unit KM_ControlsProgressBar;
|
||||
{$I KaM_Remake.inc}
|
||||
interface
|
||||
uses
|
||||
Generics.Collections,
|
||||
KromOGLUtils,
|
||||
Classes, Controls,
|
||||
KM_Controls,
|
||||
KM_RenderUI,
|
||||
KM_ResFonts,
|
||||
KM_Points,
|
||||
KM_CommonTypes;
|
||||
|
||||
|
||||
type
|
||||
// Abstract Progress bar
|
||||
TKMProgressBarAbstract = class abstract(TKMControl)
|
||||
private
|
||||
fFont: TKMFont;
|
||||
fTextAlign: TKMTextAlign;
|
||||
protected
|
||||
procedure PaintBar; virtual; abstract;
|
||||
public
|
||||
//CaptionLeft and CaptionRight are shown to the left and right from main Caption. Use them only with taCenter
|
||||
Caption, CaptionLeft, CaptionRight: UnicodeString;
|
||||
FontColor: TColor4;
|
||||
TextYOffset: Integer;
|
||||
constructor Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; aFont: TKMFont = fntMini);
|
||||
procedure SetCaptions(const aCaptionLeft, aCaption, aCaptionRight: UnicodeString);
|
||||
procedure Paint; override;
|
||||
end;
|
||||
|
||||
|
||||
{Percent bar}
|
||||
TKMPercentBar = class(TKMProgressBarAbstract)
|
||||
private
|
||||
fPosition: Single;
|
||||
fSeam: Single;
|
||||
fMainColor: Cardinal;
|
||||
fAddColor: Cardinal;
|
||||
procedure SetPosition(aValue: Single);
|
||||
procedure SetSeam(aValue: Single);
|
||||
protected
|
||||
procedure PaintBar; override;
|
||||
public
|
||||
constructor Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; aFont: TKMFont = fntMini);
|
||||
property Seam: Single read fSeam write SetSeam;
|
||||
property Position: Single read fPosition write SetPosition;
|
||||
property MainColor: Cardinal read fMainColor write fMainColor;
|
||||
property AddColor: Cardinal read fAddColor write fAddColor;
|
||||
end;
|
||||
|
||||
|
||||
TKMReplayBar = class(TKMPercentBar)
|
||||
private
|
||||
fIsDirty: Boolean; //True is Marks are not not sorted yet
|
||||
fPosition: Integer;
|
||||
fPeacetime: Integer;
|
||||
fMaxValue: Integer;
|
||||
fHighlightMark: Integer;
|
||||
fMarks: TList<Integer>;
|
||||
fMarksPattern: Word;
|
||||
fOnMarkClick: TIntegerEvent;
|
||||
fHintResText: Word;
|
||||
procedure TrySortMarks;
|
||||
procedure SetPosition(aValue: Integer);
|
||||
procedure SetPeacetime(aValue: Integer);
|
||||
procedure SetMaxValue(aValue: Integer);
|
||||
protected
|
||||
procedure ControlMouseMove(Sender: TObject; X, Y: Integer; Shift: TShiftState);
|
||||
procedure PaintBar; override;
|
||||
public
|
||||
constructor Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; aFont: TKMFont = fntMini); overload;
|
||||
constructor Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight, aPosition, aPeacetime, aMaxValue: Integer; aFont: TKMFont = fntMini); overload;
|
||||
destructor Destroy; override;
|
||||
|
||||
property Position: Integer read fPosition;
|
||||
property Peacetime: Integer read fPeacetime;
|
||||
property MaxValue: Integer read fMaxValue write SetMaxValue;
|
||||
|
||||
procedure SetParameters(aPosition, aPeacetime, aMaxValue: Integer);
|
||||
property MarksPattern: Word read fMarksPattern write fMarksPattern;
|
||||
property HintResText: Word read fHintResText write fHintResText;
|
||||
// property Marks: TList<Integer> read GetMarks;
|
||||
|
||||
procedure AddMark(aMark: Integer);
|
||||
procedure Clear;
|
||||
|
||||
property OnMarkClick: TIntegerEvent read fOnMarkClick write fOnMarkClick;
|
||||
|
||||
procedure MouseMove(X,Y: Integer; Shift: TShiftState); override;
|
||||
procedure MouseUp (X,Y: Integer; Shift: TShiftState; Button: TMouseButton); override;
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
uses
|
||||
SysUtils,
|
||||
Math,
|
||||
KM_Resource, KM_ResTexts,
|
||||
KM_Defaults,
|
||||
KM_CommonUtils;
|
||||
|
||||
|
||||
{ TKMProgressBarAbstract }
|
||||
constructor TKMProgressBarAbstract.Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; aFont: TKMFont = fntMini);
|
||||
begin
|
||||
inherited Create(aParent, aLeft, aTop, aWidth, aHeight);
|
||||
fFont := aFont;
|
||||
FontColor := $FFFFFFFF;
|
||||
fTextAlign := taCenter;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMProgressBarAbstract.SetCaptions(const aCaptionLeft, aCaption, aCaptionRight: UnicodeString);
|
||||
begin
|
||||
CaptionLeft := aCaptionLeft;
|
||||
Caption := aCaption;
|
||||
CaptionRight := aCaptionRight;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMProgressBarAbstract.Paint;
|
||||
var
|
||||
captionSize: TKMPoint;
|
||||
begin
|
||||
inherited;
|
||||
|
||||
PaintBar;
|
||||
|
||||
//Now draw text over the bar, if it is required
|
||||
if Caption <> '' then
|
||||
begin
|
||||
//Shadow
|
||||
TKMRenderUI.WriteText(AbsLeft + 2, (AbsTop + Height div 2)+TextYOffset-4,
|
||||
Width-4, Caption, fFont, fTextAlign, $FF000000);
|
||||
//Text
|
||||
TKMRenderUI.WriteText(AbsLeft + 1, (AbsTop + Height div 2)+TextYOffset-5,
|
||||
Width-4, Caption, fFont, fTextAlign, FontColor);
|
||||
end;
|
||||
|
||||
if (CaptionLeft <> '') or (CaptionRight <> '') then
|
||||
captionSize := gRes.Fonts[fFont].GetTextSize(Caption);
|
||||
|
||||
if CaptionLeft <> '' then
|
||||
begin
|
||||
//Shadow
|
||||
TKMRenderUI.WriteText(AbsLeft + 2, (AbsTop + Height div 2)+TextYOffset-4,
|
||||
(Width-4 - captionSize.X) div 2, CaptionLeft, fFont, taRight, $FF000000);
|
||||
//Text
|
||||
TKMRenderUI.WriteText(AbsLeft + 1, (AbsTop + Height div 2)+TextYOffset-5,
|
||||
(Width-4 - captionSize.X) div 2, CaptionLeft, fFont, taRight, FontColor);
|
||||
end;
|
||||
|
||||
if CaptionRight <> '' then
|
||||
begin
|
||||
//Shadow
|
||||
TKMRenderUI.WriteText(AbsLeft + 2 + ((Width-4 + captionSize.X) div 2), (AbsTop + Height div 2)+TextYOffset-4,
|
||||
(Width-4 - captionSize.X) div 2, CaptionRight, fFont, taLeft, $FF000000);
|
||||
//Text
|
||||
TKMRenderUI.WriteText(AbsLeft + 1 + ((Width-4 + captionSize.X) div 2), (AbsTop + Height div 2)+TextYOffset-5,
|
||||
(Width-4 - captionSize.X) div 2, CaptionRight, fFont, taLeft, FontColor);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
{ TKMPercentBar }
|
||||
constructor TKMPercentBar.Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; aFont: TKMFont = fntMini);
|
||||
begin
|
||||
inherited;
|
||||
|
||||
fMainColor := icBarColorGreen;
|
||||
fAddColor := icBarColorBlue;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMPercentBar.SetPosition(aValue: Single);
|
||||
begin
|
||||
fPosition := EnsureRange(aValue, 0, 1);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMPercentBar.SetSeam(aValue: Single);
|
||||
begin
|
||||
fSeam := EnsureRange(aValue, 0, 1);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMPercentBar.PaintBar;
|
||||
begin
|
||||
TKMRenderUI.WritePercentBar(AbsLeft, AbsTop, Width, Height, fPosition, fSeam, fMainColor, fAddColor);
|
||||
end;
|
||||
|
||||
|
||||
{ TKMReplayBar }
|
||||
constructor TKMReplayBar.Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; aFont: TKMFont = fntMini);
|
||||
begin
|
||||
Create(aParent, aLeft, aTop, aWidth, aHeight, 0, MaxInt, MaxInt, aFont);
|
||||
end;
|
||||
|
||||
|
||||
constructor TKMReplayBar.Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight, aPosition, aPeacetime, aMaxValue: Integer;
|
||||
aFont: TKMFont = fntMini);
|
||||
begin
|
||||
inherited Create(aParent, aLeft, aTop, aWidth, aHeight, aFont);
|
||||
|
||||
SetParameters(aPosition, aPeacetime, aMaxValue);
|
||||
fMarksPattern := $CF3; //Looks good for 25px height bar
|
||||
|
||||
fHighlightMark := -1;
|
||||
|
||||
fMarks := TList<Integer>.Create;
|
||||
|
||||
aParent.MasterControl.SubscribeOnOtherMouseMove(ControlMouseMove);
|
||||
end;
|
||||
|
||||
|
||||
destructor TKMReplayBar.Destroy;
|
||||
begin
|
||||
FreeAndNil(fMarks);
|
||||
|
||||
inherited;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMReplayBar.SetParameters(aPosition, aPeacetime, aMaxValue: Integer);
|
||||
begin
|
||||
//Apply setters
|
||||
MaxValue := aMaxValue; //Should be first, since we restrict Position and PT with MaxValue
|
||||
SetPosition(aPosition);
|
||||
SetPeacetime(aPeacetime);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMReplayBar.AddMark(aMark: Integer);
|
||||
begin
|
||||
if Self = nil then Exit;
|
||||
|
||||
Assert(fMarks <> nil, 'Marks is not initilized');
|
||||
|
||||
fMarks.Add(aMark);
|
||||
fIsDirty := True;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMReplayBar.SetPosition(aValue: Integer);
|
||||
begin
|
||||
fPosition := EnsureRange(aValue, 0, fMaxValue);
|
||||
// Caption := IntToStr(fPosition);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMReplayBar.SetPeacetime(aValue: Integer);
|
||||
begin
|
||||
fPeacetime := EnsureRange(aValue, 0, MaxInt);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMReplayBar.SetMaxValue(aValue: Integer);
|
||||
begin
|
||||
fMaxValue := EnsureRange(aValue, 1, MaxInt);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMReplayBar.MouseMove(X,Y: Integer; Shift: TShiftState);
|
||||
const
|
||||
MAX_DIST_PERCENT = 0.02;
|
||||
var
|
||||
pos, bestDist, dist: Integer;
|
||||
mark, bestMark: Integer;
|
||||
begin
|
||||
inherited;
|
||||
pos := Round((X - AbsLeft) / Width * MaxValue);
|
||||
|
||||
bestDist := MaxInt;
|
||||
bestMark := -1;
|
||||
|
||||
TrySortMarks;
|
||||
for mark in fMarks do
|
||||
begin
|
||||
dist := Abs(pos - mark);
|
||||
if dist < MAX_DIST_PERCENT*fMaxValue then
|
||||
begin
|
||||
if dist < bestDist then
|
||||
begin
|
||||
bestDist := dist;
|
||||
bestMark := mark;
|
||||
end else
|
||||
Break; //List is sorted, we have found what we need
|
||||
end;
|
||||
end;
|
||||
|
||||
fHighlightMark := bestMark;
|
||||
|
||||
if fHighlightMark <> -1 then
|
||||
begin
|
||||
Hint := Format(gResTexts[fHintResText], [TickToTimeStr(fHighlightMark)]);
|
||||
// Caption := IntToStr(fPosition) + ' (' + IntToStr(fHighlightMark) + ')';
|
||||
end
|
||||
else
|
||||
begin
|
||||
Hint := '';
|
||||
// Caption := IntToStr(fPosition);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMReplayBar.MouseUp(X: Integer; Y: Integer; Shift: TShiftState; Button: TMouseButton);
|
||||
begin
|
||||
inherited;
|
||||
|
||||
if (fHighlightMark <> -1) and Assigned(fOnMarkClick) then
|
||||
fOnMarkClick(fHighlightMark);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMReplayBar.TrySortMarks;
|
||||
begin
|
||||
if fIsDirty then
|
||||
begin
|
||||
fIsDirty := False;
|
||||
fMarks.Sort;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMReplayBar.PaintBar;
|
||||
begin
|
||||
inherited;
|
||||
TKMRenderUI.WriteReplayBar(AbsLeft, AbsTop, Width, Height, fPosition, fPeacetime, fMaxValue, fMarks, MarksPattern, fHighlightMark);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMReplayBar.Clear;
|
||||
begin
|
||||
fMarks.Clear;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMReplayBar.ControlMouseMove(Sender: TObject; X, Y: Integer; Shift: TShiftState);
|
||||
begin
|
||||
inherited;
|
||||
if not InRange(X, AbsLeft, AbsRight)
|
||||
or not InRange(Y, AbsTop, AbsBottom) then
|
||||
fHighlightMark := -1;
|
||||
end;
|
||||
|
||||
|
||||
end.
|
||||
|
||||
@@ -0,0 +1,823 @@
|
||||
unit KM_ControlsScroll;
|
||||
{$I KaM_Remake.inc}
|
||||
interface
|
||||
uses
|
||||
Classes, Controls,
|
||||
KM_Controls, KM_ControlsBase,
|
||||
KM_RenderUI,
|
||||
KM_Points;
|
||||
|
||||
|
||||
type
|
||||
TKMScrollAxis = (saVertical, saHorizontal);
|
||||
TKMScrollStyle = (ssGame, ssCommon);
|
||||
TKMScrollAxisSet = set of TKMScrollAxis;
|
||||
|
||||
TKMScrollBar = class(TKMPanel)
|
||||
private
|
||||
fScrollAxis: TKMScrollAxis;
|
||||
fStyle: TKMButtonStyle;
|
||||
fMinValue: Integer;
|
||||
fMaxValue: Integer;
|
||||
fPosition: Integer;
|
||||
fThumbPos: Integer; //Position of the thumb
|
||||
fThumbSize: Word; //Length of the thumb
|
||||
fOffset: Integer;
|
||||
fScrollDec: TKMButton;
|
||||
fScrollInc: TKMButton;
|
||||
fOnChange: TNotifyEvent;
|
||||
procedure SetMinValue(Value: Integer);
|
||||
procedure SetMaxValue(Value: Integer);
|
||||
procedure SetPosition(Value: Integer);
|
||||
procedure IncPosition(Sender: TObject);
|
||||
procedure DecPosition(Sender: TObject);
|
||||
procedure UpdateThumbPos;
|
||||
procedure UpdateThumbSize;
|
||||
protected
|
||||
procedure SetHeight(aValue: Integer); override;
|
||||
procedure SetWidth(aValue: Integer); override;
|
||||
procedure SetEnabled(aValue: Boolean); override;
|
||||
function DoHandleMouseWheelByDefault: Boolean; override;
|
||||
public
|
||||
BackAlpha: Single; //Alpha of background (usually 0.5, dropbox 1)
|
||||
EdgeAlpha: Single; //Alpha of background outline (usually 1)
|
||||
WheelStep: Word;
|
||||
|
||||
constructor Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; aScrollAxis: TKMScrollAxis;
|
||||
aStyle: TKMButtonStyle; aScrollStyle: TKMScrollStyle = ssGame);
|
||||
property MinValue: Integer read fMinValue write SetMinValue;
|
||||
property MaxValue: Integer read fMaxValue write SetMaxValue;
|
||||
property Position: Integer read fPosition write SetPosition;
|
||||
procedure MouseDown(X,Y: Integer; Shift: TShiftState; Button: TMouseButton); override;
|
||||
procedure MouseMove(X,Y: Integer; Shift: TShiftState); override;
|
||||
procedure MouseWheel(Sender: TObject; WheelSteps: Integer; var aHandled: Boolean); override;
|
||||
property OnChange: TNotifyEvent read fOnChange write fOnChange;
|
||||
procedure Paint; override;
|
||||
end;
|
||||
|
||||
|
||||
TKMScrollPanel = class(TKMPanel)
|
||||
private
|
||||
fChildsPanel: TKMPanel;
|
||||
|
||||
fClipRect: TKMRect;
|
||||
fScrollBarH: TKMScrollBar;
|
||||
fScrollBarV: TKMScrollBar;
|
||||
fScrollAxisSet: TKMScrollAxisSet;
|
||||
|
||||
fPadding: TKMRect;
|
||||
fScrollV_PadTop: Integer;
|
||||
fScrollV_PadBottom: Integer;
|
||||
fScrollV_PadLeft: Integer;
|
||||
|
||||
procedure UpdateScrolls(Sender: TObject; aValue: Boolean); overload;
|
||||
procedure UpdateScrolls(Sender: TObject); overload;
|
||||
procedure UpdateScrollV; overload;
|
||||
procedure UpdateScrollV(aChildsRect: TKMRect); overload;
|
||||
procedure UpdateScrollV(Sender: TObject; const aValue: Integer); overload;
|
||||
procedure UpdateScrollH; overload;
|
||||
procedure UpdateScrollH(aChildsRect: TKMRect); overload;
|
||||
procedure UpdateScrollH(Sender: TObject; const aValue: Integer); overload;
|
||||
procedure ScrollChanged(Sender: TObject);
|
||||
function GetChildsRect: TKMRect;
|
||||
|
||||
function AllowScrollV: Boolean;
|
||||
function AllowScrollH: Boolean;
|
||||
procedure SetScrollVPadTop(const Value: Integer);
|
||||
procedure SetScrollVPadBottom(const Value: Integer);
|
||||
|
||||
procedure SetScrollVPadLeft(const Value: Integer);
|
||||
protected
|
||||
procedure SetVisible(aValue: Boolean); override;
|
||||
procedure SetHitable(const aValue: Boolean); override;
|
||||
|
||||
procedure SetLeft(aValue: Integer); override;
|
||||
procedure SetTop(aValue: Integer); override;
|
||||
procedure SetHeight(aValue: Integer); override;
|
||||
procedure SetWidth(aValue: Integer); override;
|
||||
|
||||
function GetDrawRect: TKMRect; override;
|
||||
|
||||
function GetAbsDrawLeft: Integer; override;
|
||||
function GetAbsDrawTop: Integer; override;
|
||||
function GetAbsDrawRight: Integer; override;
|
||||
function GetAbsDrawBottom: Integer; override;
|
||||
|
||||
procedure UpdateVisibility; override;
|
||||
public
|
||||
constructor Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; aScrollAxisSet: TKMScrollAxisSet;
|
||||
aStyle: TKMButtonStyle; aScrollStyle: TKMScrollStyle);
|
||||
|
||||
property ScrollH: TKMScrollBar read fScrollBarH;
|
||||
property ScrollV: TKMScrollBar read fScrollBarV;
|
||||
|
||||
function AddChild(aChild: TKMControl): Integer; override;
|
||||
property ClipRect: TKMRect read fClipRect;
|
||||
property Padding: TKMRect read fPadding write fPadding;
|
||||
property ScrollV_PadTop: Integer read fScrollV_PadTop write SetScrollVPadTop;
|
||||
property ScrollV_PadLeft: Integer read fScrollV_PadLeft write SetScrollVPadLeft;
|
||||
property ScrollV_PadBottom: Integer read fScrollV_PadBottom write SetScrollVPadBottom;
|
||||
|
||||
procedure Show; override;
|
||||
|
||||
procedure MouseWheel(Sender: TObject; WheelSteps: Integer; var aHandled: Boolean); override;
|
||||
|
||||
procedure ClipY;
|
||||
procedure UnClipY;
|
||||
|
||||
procedure UpdateScrolls; overload;
|
||||
|
||||
procedure Paint; override;
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
uses
|
||||
Math,
|
||||
KM_ResTypes;
|
||||
|
||||
|
||||
{ TKMScrollBar }
|
||||
constructor TKMScrollBar.Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; aScrollAxis: TKMScrollAxis;
|
||||
aStyle: TKMButtonStyle; aScrollStyle: TKMScrollStyle = ssGame);
|
||||
var
|
||||
decId, incId: Integer;
|
||||
begin
|
||||
inherited Create(aParent, aLeft, aTop, aWidth, aHeight);
|
||||
BackAlpha := 0.5;
|
||||
EdgeAlpha := 0.75;
|
||||
fScrollAxis := aScrollAxis;
|
||||
fMinValue := 0;
|
||||
fMaxValue := 10;
|
||||
fPosition := 0;
|
||||
fStyle := aStyle;
|
||||
WheelStep := 1;
|
||||
|
||||
if aScrollAxis = saVertical then
|
||||
begin
|
||||
fScrollDec := TKMButton.Create(Self, 0, 0, aWidth, aWidth, 591, rxGui, aStyle);
|
||||
fScrollInc := TKMButton.Create(Self, 0, aHeight-aWidth, aWidth, aWidth, 590, rxGui, aStyle);
|
||||
fScrollDec.Anchors := [anLeft, anTop, anRight];
|
||||
fScrollInc.Anchors := [anLeft, anRight, anBottom];
|
||||
end;
|
||||
if aScrollAxis = saHorizontal then
|
||||
begin
|
||||
if aScrollStyle = ssGame then
|
||||
begin
|
||||
decId := 2;
|
||||
incId := 3;
|
||||
end else begin
|
||||
decId := 674;
|
||||
incId := 675;
|
||||
end;
|
||||
fScrollDec := TKMButton.Create(Self, 0, 0, aHeight, aHeight, decId, rxGui, aStyle);
|
||||
fScrollInc := TKMButton.Create(Self, aWidth-aHeight, 0, aHeight, aHeight, incId, rxGui, aStyle);
|
||||
fScrollDec.Anchors := [anLeft, anTop, anBottom];
|
||||
fScrollInc.Anchors := [anTop, anRight, anBottom];
|
||||
end;
|
||||
fScrollDec.OnClick := DecPosition;
|
||||
fScrollDec.OnMouseWheel := MouseWheel;
|
||||
fScrollInc.OnClick := IncPosition;
|
||||
fScrollInc.OnMouseWheel := MouseWheel;
|
||||
UpdateThumbSize;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMScrollBar.SetHeight(aValue: Integer);
|
||||
begin
|
||||
inherited;
|
||||
|
||||
//Update Thumb size
|
||||
UpdateThumbSize;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMScrollBar.SetWidth(aValue: Integer);
|
||||
begin
|
||||
inherited;
|
||||
|
||||
//Update Thumb size
|
||||
UpdateThumbSize;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMScrollBar.SetEnabled(aValue: Boolean);
|
||||
begin
|
||||
inherited;
|
||||
fScrollDec.Enabled := Enabled;
|
||||
fScrollInc.Enabled := Enabled;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMScrollBar.SetMinValue(Value: Integer);
|
||||
begin
|
||||
fMinValue := Max(0, Value);
|
||||
Enabled := (fMaxValue > fMinValue);
|
||||
SetPosition(fPosition);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMScrollBar.SetMaxValue(Value: Integer);
|
||||
begin
|
||||
fMaxValue := Max(0, Value);
|
||||
Enabled := (fMaxValue > fMinValue);
|
||||
SetPosition(fPosition);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMScrollBar.SetPosition(Value: Integer);
|
||||
begin
|
||||
fPosition := EnsureRange(Value, fMinValue, fMaxValue);
|
||||
UpdateThumbPos;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMScrollBar.IncPosition(Sender: TObject);
|
||||
begin
|
||||
SetPosition(fPosition + WheelStep);
|
||||
|
||||
if Assigned(fOnChange) then
|
||||
fOnChange(Self);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMScrollBar.DecPosition(Sender: TObject);
|
||||
begin
|
||||
SetPosition(fPosition - WheelStep);
|
||||
|
||||
if Assigned(fOnChange) then
|
||||
fOnChange(Self);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMScrollBar.UpdateThumbPos;
|
||||
begin
|
||||
fThumbPos := 0;
|
||||
|
||||
if fMaxValue > fMinValue then
|
||||
case fScrollAxis of
|
||||
saVertical: fThumbPos := (fPosition-fMinValue)*(Height-Width*2-fThumbSize) div (fMaxValue-fMinValue);
|
||||
saHorizontal: fThumbPos := (fPosition-fMinValue)*(Width-Height*2-fThumbSize) div (fMaxValue-fMinValue);
|
||||
end
|
||||
else
|
||||
case fScrollAxis of
|
||||
saVertical: fThumbPos := Math.max((Height-Width*2-fThumbSize),0) div 2;
|
||||
saHorizontal: fThumbPos := Math.max((Width-Height*2-fThumbSize),0) div 2;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMScrollBar.UpdateThumbSize;
|
||||
begin
|
||||
case fScrollAxis of
|
||||
saVertical: fThumbSize := Math.max(0, (Height-2*Width)) div 4;
|
||||
saHorizontal: fThumbSize := Math.max(0, (Width-2*Height)) div 4;
|
||||
end;
|
||||
|
||||
//If size has changed, then Pos needs to be updated as well (depends on it)
|
||||
UpdateThumbPos;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMScrollBar.MouseDown(X,Y: Integer; Shift: TShiftState; Button: TMouseButton);
|
||||
var
|
||||
T: Integer;
|
||||
begin
|
||||
inherited;
|
||||
|
||||
fOffset := 0;
|
||||
case fScrollAxis of
|
||||
saVertical: begin
|
||||
T := Y - AbsTop - Width - fThumbPos;
|
||||
if InRange(T, 0, fThumbSize) then
|
||||
fOffset := T - fThumbSize div 2;
|
||||
end;
|
||||
saHorizontal: begin
|
||||
T := X - AbsLeft - Height - fThumbPos;
|
||||
if InRange(T, 0, fThumbSize) then
|
||||
fOffset := T - fThumbSize div 2;
|
||||
end;
|
||||
end;
|
||||
|
||||
MouseMove(X,Y,Shift); //Will change Position and call OnChange event
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMScrollBar.MouseMove(X,Y: Integer; Shift: TShiftState);
|
||||
var
|
||||
newPos: Integer;
|
||||
T: Integer;
|
||||
begin
|
||||
inherited;
|
||||
if not (ssLeft in Shift) then Exit;
|
||||
|
||||
newPos := fPosition;
|
||||
|
||||
case fScrollAxis of
|
||||
saVertical:
|
||||
begin
|
||||
T := Y - fOffset - AbsTop - Width;
|
||||
if InRange(T, 0, Height - Width * 2) then
|
||||
newPos := Round(fMinValue+((T - fThumbSize / 2) / (Height-Width*2-fThumbSize)) * (fMaxValue - fMinValue) );
|
||||
end;
|
||||
|
||||
saHorizontal:
|
||||
begin
|
||||
T := X - fOffset - AbsLeft - Height;
|
||||
if InRange(T, 0, Width - Height * 2) then
|
||||
newPos := Round(fMinValue+((T - fThumbSize / 2) / (Width-Height*2-fThumbSize)) * (fMaxValue - fMinValue) );
|
||||
end;
|
||||
end;
|
||||
|
||||
if newPos <> fPosition then
|
||||
begin
|
||||
SetPosition(newPos);
|
||||
if Assigned(fOnChange) then
|
||||
fOnChange(Self);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
function TKMScrollBar.DoHandleMouseWheelByDefault: Boolean;
|
||||
begin
|
||||
Result := False;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMScrollBar.MouseWheel(Sender: TObject; WheelSteps: Integer; var aHandled: Boolean);
|
||||
begin
|
||||
inherited;
|
||||
|
||||
if aHandled then Exit;
|
||||
|
||||
aHandled := WheelSteps <> 0;
|
||||
|
||||
if aHandled then
|
||||
begin
|
||||
Position := Position - WheelStep * WheelSteps;
|
||||
|
||||
if Assigned(fOnChange) then
|
||||
fOnChange(Self);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMScrollBar.Paint;
|
||||
var
|
||||
buttonState: TKMButtonStateSet;
|
||||
begin
|
||||
inherited;
|
||||
|
||||
case fScrollAxis of
|
||||
saVertical: TKMRenderUI.WriteBevel(AbsLeft, AbsTop+Width, Width, Height - Width*2, EdgeAlpha, BackAlpha);
|
||||
saHorizontal: TKMRenderUI.WriteBevel(AbsLeft+Height, AbsTop, Width - Height*2, Height, EdgeAlpha, BackAlpha);
|
||||
end;
|
||||
|
||||
if fMaxValue > fMinValue then
|
||||
buttonState := []
|
||||
else
|
||||
buttonState := [bsDisabled];
|
||||
|
||||
if not (bsDisabled in buttonState) then //Only show thumb when usable
|
||||
case fScrollAxis of
|
||||
saVertical: TKMRenderUI.Write3DButton(AbsLeft,AbsTop+Width+fThumbPos,Width,fThumbSize,rxGui,0,$FFFF00FF,buttonState,fStyle);
|
||||
saHorizontal: TKMRenderUI.Write3DButton(AbsLeft+Height+fThumbPos,AbsTop,fThumbSize,Height,rxGui,0,$FFFF00FF,buttonState,fStyle);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
{ TKMScrollPanel }
|
||||
constructor TKMScrollPanel.Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; aScrollAxisSet: TKMScrollAxisSet;
|
||||
aStyle: TKMButtonStyle; aScrollStyle: TKMScrollStyle);
|
||||
begin
|
||||
inherited Create(aParent, aLeft, aTop, aWidth, aHeight);
|
||||
|
||||
fPadding := KMRect(0, 0, 0, 0);
|
||||
|
||||
fScrollAxisSet := aScrollAxisSet;
|
||||
|
||||
fChildsPanel := TKMPanel.Create(Self, aLeft, aTop, aWidth, aHeight);
|
||||
fChildsPanel.AnchorsStretch;
|
||||
fChildsPanel.IsHitTestUseDrawRect := True; // We want DrawRect to be used on the ScrollPanel
|
||||
|
||||
// We should create scroll bars on Parent panel, since we move panel there and back and scroll should not be scrolled as well
|
||||
fScrollBarH := TKMScrollBar.Create(aParent, aLeft, aTop + aHeight - 20, aWidth, 20, saHorizontal, aStyle, aScrollStyle);
|
||||
fScrollBarH.Hide;
|
||||
fScrollBarH.OnChange := ScrollChanged;
|
||||
fScrollBarH.WheelStep := 10;
|
||||
|
||||
fScrollBarV := TKMScrollBar.Create(aParent, aLeft + aWidth - 20, aTop, 20, aHeight, saVertical, aStyle, aScrollStyle);
|
||||
fScrollBarV.Hide;
|
||||
fScrollBarV.OnChange := ScrollChanged;
|
||||
fScrollBarV.WheelStep := 10;
|
||||
|
||||
IsHitTestUseDrawRect := True; // We want DrawRect to be used on the ScrollPanel
|
||||
|
||||
fScrollV_PadTop := 0;
|
||||
fScrollV_PadBottom := 0;
|
||||
|
||||
fClipRect := KMRect(Left, Top, Right, Bottom);
|
||||
end;
|
||||
|
||||
|
||||
function TKMScrollPanel.AddChild(aChild: TKMControl): Integer;
|
||||
begin
|
||||
aChild.IsHitTestUseDrawRect := True;
|
||||
if fChildsPanel = nil then
|
||||
Result := inherited AddChild(aChild)
|
||||
else
|
||||
begin
|
||||
Result := fChildsPanel.AddChild(aChild);
|
||||
|
||||
aChild.OnHeightChange := UpdateScrollV;
|
||||
aChild.OnWidthChange := UpdateScrollH;
|
||||
aChild.OnPositionSet := UpdateScrolls;
|
||||
aChild.OnChangeVisibility := UpdateScrolls;
|
||||
aChild.OnChangeEnableStatus := UpdateScrolls;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
//Update View is usefull when use Padding Left or Top
|
||||
//To show panel properly initially. Beeter to invoke from Show Element
|
||||
procedure TKMScrollPanel.UpdateVisibility;
|
||||
begin
|
||||
inherited;
|
||||
if Visible
|
||||
and ((fPadding.Left > 0) or (fPadding.Top > 0)) then //Update visibility only when left/top padding > 0
|
||||
begin
|
||||
if saHorizontal in fScrollAxisSet then
|
||||
begin
|
||||
fScrollBarH.Position := fPadding.Left;
|
||||
ScrollChanged(fScrollBarH);
|
||||
end;
|
||||
|
||||
if saVertical in fScrollAxisSet then
|
||||
begin
|
||||
fScrollBarV.Position := fPadding.Top;
|
||||
ScrollChanged(fScrollBarV);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMScrollPanel.MouseWheel(Sender: TObject; WheelSteps: Integer; var aHandled: Boolean);
|
||||
begin
|
||||
if (saVertical in fScrollAxisSet) and fScrollBarV.Visible then
|
||||
fScrollBarV.MouseWheel(Sender, WheelSteps, aHandled)
|
||||
else if (saHorizontal in fScrollAxisSet) and fScrollBarH.Visible then
|
||||
fScrollBarH.MouseWheel(Sender, WheelSteps, aHandled)
|
||||
else
|
||||
inherited;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMScrollPanel.ScrollChanged(Sender: TObject);
|
||||
var
|
||||
oldValue: Integer;
|
||||
begin
|
||||
if Sender = fScrollBarH then
|
||||
begin
|
||||
oldValue := Left;
|
||||
Left := fClipRect.Left - fScrollBarH.Position + fPadding.Left;
|
||||
//To compensate changes in SetLeft
|
||||
Inc(fClipRect.Left, oldValue - Left);
|
||||
Inc(fClipRect.Right, oldValue - Left);
|
||||
end else
|
||||
if Sender = fScrollBarV then
|
||||
begin
|
||||
oldValue := Top;
|
||||
Top := fClipRect.Top - fScrollBarV.Position + fPadding.Top;
|
||||
//To compensate changes in SetTop
|
||||
Inc(fClipRect.Top, oldValue - Top);
|
||||
Inc(fClipRect.Bottom, oldValue - Top);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMScrollPanel.UpdateScrolls(Sender: TObject; aValue: Boolean);
|
||||
begin
|
||||
// No need to update scrolls if panel is not visible
|
||||
// In case we've just hide panel then DrawRect is RECT_ZERO and scrools will be set to 0 position
|
||||
if not Visible then Exit;
|
||||
|
||||
if (Sender <> fScrollBarH) and (Sender <> fScrollBarV) then
|
||||
UpdateScrolls(nil);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMScrollPanel.UpdateScrollH(Sender: TObject; const aValue: Integer);
|
||||
begin
|
||||
if (Sender <> fScrollBarH) then
|
||||
UpdateScrollH;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMScrollPanel.UpdateScrollH;
|
||||
begin
|
||||
if not (saHorizontal in fScrollAxisSet) then Exit;
|
||||
|
||||
UpdateScrollH(GetChildsRect);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMScrollPanel.UpdateScrollH(aChildsRect: TKMRect);
|
||||
var
|
||||
newPos: Integer;
|
||||
showScroll: Boolean;
|
||||
begin
|
||||
if not (saHorizontal in fScrollAxisSet) then Exit;
|
||||
|
||||
showScroll := False;
|
||||
|
||||
if aChildsRect.Width + fPadding.Left + fPadding.Right > fClipRect.Width then
|
||||
begin
|
||||
fScrollBarH.MaxValue := aChildsRect.Width - fClipRect.Width + fPadding.Left + fPadding.Right;
|
||||
newPos := fClipRect.Left - Left;
|
||||
|
||||
if newPos > fScrollBarH.MaxValue then
|
||||
SetLeftSilently(Left + newPos - fScrollBarH.MaxValue); //Slightly move panel to the top, when resize near maxvalue position
|
||||
showScroll := True;
|
||||
end else begin
|
||||
fScrollBarH.Position := 0;
|
||||
if Left <> fClipRect.Left then
|
||||
SetLeftSilently(fClipRect.Left); //Set directrly to avoid SetLeft call
|
||||
end;
|
||||
|
||||
fScrollBarH.Width := Width;
|
||||
if showScroll <> fScrollBarH.Visible then
|
||||
begin
|
||||
fScrollBarH.Visible := showScroll;
|
||||
fChildsPanel.Height := Height - 20*Byte(showScroll);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMScrollPanel.UpdateScrollV(Sender: TObject; const aValue: Integer);
|
||||
begin
|
||||
if (Sender <> fScrollBarV) then
|
||||
UpdateScrollV;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMScrollPanel.UpdateScrollV;
|
||||
begin
|
||||
if not (saVertical in fScrollAxisSet) then Exit;
|
||||
|
||||
UpdateScrollV(GetChildsRect);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMScrollPanel.UpdateScrollV(aChildsRect: TKMRect);
|
||||
var
|
||||
newPos: Integer;
|
||||
showScroll: Boolean;
|
||||
begin
|
||||
if not (saVertical in fScrollAxisSet) then Exit;
|
||||
|
||||
//Do not set Visible, avoid trigger OnChangeVisibility
|
||||
showScroll := False;
|
||||
|
||||
if aChildsRect.Height + fPadding.Top + fPadding.Bottom > fClipRect.Height then
|
||||
begin
|
||||
fScrollBarV.MaxValue := aChildsRect.Height - fClipRect.Height + fPadding.Top + fPadding.Bottom;
|
||||
newPos := fClipRect.Top - Top;
|
||||
|
||||
if newPos > fScrollBarV.MaxValue then
|
||||
SetTopSilently(Top + newPos - fScrollBarV.MaxValue); //Slightly move panel to the top, when resize near maxvalue position
|
||||
showScroll := True;
|
||||
end else begin
|
||||
fScrollBarV.Position := 0;
|
||||
if Top <> fClipRect.Top then
|
||||
SetTopSilently(fClipRect.Top); //Set directrly to avoid SetTop call
|
||||
end;
|
||||
|
||||
fScrollBarV.Height := Height - fScrollV_PadTop - fScrollV_PadBottom;
|
||||
if showScroll <> fScrollBarV.Visible then
|
||||
begin
|
||||
fScrollBarV.Visible := showScroll;
|
||||
fChildsPanel.Width := Width - 20*Byte(showScroll);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMScrollPanel.UpdateScrolls;
|
||||
begin
|
||||
UpdateScrolls(nil);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMScrollPanel.UpdateScrolls(Sender: TObject);
|
||||
var
|
||||
childsRect: TKMRect;
|
||||
begin
|
||||
childsRect := GetChildsRect;
|
||||
|
||||
UpdateScrollV(childsRect);
|
||||
UpdateScrollH(childsRect);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMScrollPanel.SetLeft(aValue: Integer);
|
||||
var
|
||||
oldValue: Integer;
|
||||
begin
|
||||
oldValue := Left;
|
||||
inherited;
|
||||
|
||||
Inc(fClipRect.Left, Left - oldValue);
|
||||
Inc(fClipRect.Right, Left - oldValue);
|
||||
|
||||
UpdateScrolls(nil);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMScrollPanel.SetScrollVPadBottom(const Value: Integer);
|
||||
begin
|
||||
fScrollV_PadBottom := Value;
|
||||
fScrollBarV.Height := fScrollBarV.Height - Value;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMScrollPanel.SetScrollVPadLeft(const Value: Integer);
|
||||
begin
|
||||
fScrollV_PadLeft := Value;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMScrollPanel.SetScrollVPadTop(const Value: Integer);
|
||||
begin
|
||||
fScrollV_PadTop := Value;
|
||||
fScrollBarV.Top := fScrollBarV.Top + Value;
|
||||
fScrollBarV.Height := fScrollBarV.Height - Value;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMScrollPanel.SetTop(aValue: Integer);
|
||||
var
|
||||
oldValue: Integer;
|
||||
begin
|
||||
oldValue := Top;
|
||||
inherited;
|
||||
|
||||
Inc(fClipRect.Top, Top - oldValue);
|
||||
Inc(fClipRect.Bottom, Top - oldValue);
|
||||
|
||||
UpdateScrolls(nil);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMScrollPanel.SetVisible(aValue: Boolean);
|
||||
begin
|
||||
inherited;
|
||||
|
||||
// Hide ScrollBars with if we hide ScrollPanel
|
||||
if not aValue then
|
||||
begin
|
||||
fScrollBarH.Visible := aValue;
|
||||
fScrollBarV.Visible := aValue;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMScrollPanel.SetWidth(aValue: Integer);
|
||||
var
|
||||
oldValue: Integer;
|
||||
begin
|
||||
oldValue := Width;
|
||||
inherited;
|
||||
|
||||
Inc(fClipRect.Right, Width - oldValue);
|
||||
|
||||
fScrollBarV.Left := fClipRect.Left + Width + fScrollV_PadLeft;
|
||||
|
||||
UpdateScrolls(nil);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMScrollPanel.Show;
|
||||
begin
|
||||
inherited;
|
||||
|
||||
// We can use Visible here, since Self is he parent of ChildsPanel
|
||||
fChildsPanel.Visible := True;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMScrollPanel.SetHeight(aValue: Integer);
|
||||
var
|
||||
oldValue: Integer;
|
||||
begin
|
||||
oldValue := Height;
|
||||
inherited;
|
||||
|
||||
Inc(fClipRect.Bottom, Height - oldValue);
|
||||
|
||||
fScrollBarH.Top := fClipRect.Top + Height;
|
||||
|
||||
UpdateScrolls(nil);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMScrollPanel.SetHitable(const aValue: Boolean);
|
||||
begin
|
||||
inherited;
|
||||
|
||||
fChildsPanel.Hitable := aValue;
|
||||
end;
|
||||
|
||||
|
||||
function TKMScrollPanel.GetChildsRect: TKMRect;
|
||||
var
|
||||
I: Integer;
|
||||
begin
|
||||
Result := KMRECT_ZERO; //Zero rect, by default
|
||||
|
||||
for I := 0 to fChildsPanel.ChildCount - 1 do
|
||||
begin
|
||||
if not fChildsPanel.Childs[I].IsPainted then
|
||||
Continue;
|
||||
|
||||
if fChildsPanel.Childs[I].Left < Result.Left then
|
||||
Result.Left := fChildsPanel.Childs[I].Left;
|
||||
|
||||
if fChildsPanel.Childs[I].Top < Result.Top then
|
||||
Result.Top := fChildsPanel.Childs[I].Top;
|
||||
|
||||
if fChildsPanel.Childs[I].Right > Result.Right then
|
||||
Result.Right := fChildsPanel.Childs[I].Right;
|
||||
|
||||
if fChildsPanel.Childs[I].Bottom > Result.Bottom then
|
||||
Result.Bottom := fChildsPanel.Childs[I].Bottom;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
function TKMScrollPanel.AllowScrollV: Boolean;
|
||||
begin
|
||||
Result := saVertical in fScrollAxisSet;
|
||||
end;
|
||||
|
||||
|
||||
function TKMScrollPanel.AllowScrollH: Boolean;
|
||||
begin
|
||||
Result := saHorizontal in fScrollAxisSet;
|
||||
end;
|
||||
|
||||
|
||||
function TKMScrollPanel.GetDrawRect: TKMRect;
|
||||
begin
|
||||
Result := KMRect(AbsDrawLeft, AbsDrawTop, AbsDrawRight, AbsDrawBottom);
|
||||
end;
|
||||
|
||||
|
||||
function TKMScrollPanel.GetAbsDrawLeft: Integer;
|
||||
begin
|
||||
Result := Parent.AbsLeft + fClipRect.Left;
|
||||
end;
|
||||
|
||||
|
||||
function TKMScrollPanel.GetAbsDrawTop: Integer;
|
||||
begin
|
||||
Result := Parent.AbsTop + fClipRect.Top;
|
||||
end;
|
||||
|
||||
|
||||
function TKMScrollPanel.GetAbsDrawRight: Integer;
|
||||
begin
|
||||
Result := Parent.AbsLeft + fClipRect.Right + 20*Byte(AllowScrollV and not fScrollBarV.Visible);
|
||||
end;
|
||||
|
||||
|
||||
function TKMScrollPanel.GetAbsDrawBottom: Integer;
|
||||
begin
|
||||
Result := Parent.AbsTop + fClipRect.Bottom + 20*Byte(AllowScrollH and not fScrollBarH.Visible);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMScrollPanel.Paint;
|
||||
begin
|
||||
TKMRenderUI.SetupClipX(AbsDrawLeft, AbsDrawRight);
|
||||
TKMRenderUI.SetupClipY(AbsDrawTop, AbsDrawBottom);
|
||||
|
||||
inherited;
|
||||
|
||||
TKMRenderUI.ReleaseClipY;
|
||||
TKMRenderUI.ReleaseClipX;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMScrollPanel.ClipY;
|
||||
begin
|
||||
TKMRenderUI.SetupClipY(AbsDrawTop, AbsDrawBottom);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMScrollPanel.UnClipY;
|
||||
begin
|
||||
TKMRenderUI.ReleaseClipY;
|
||||
end;
|
||||
|
||||
|
||||
end.
|
||||
|
||||
@@ -0,0 +1,455 @@
|
||||
unit KM_ControlsSwitch;
|
||||
{$I KaM_Remake.inc}
|
||||
interface
|
||||
uses
|
||||
Classes, Controls,
|
||||
KromOGLUtils,
|
||||
KM_Controls,
|
||||
KM_ResFonts;
|
||||
|
||||
|
||||
type
|
||||
TKMCheckBoxState = (cbsUnchecked, cbsSemiChecked, cbsChecked);
|
||||
|
||||
{ Checkbox }
|
||||
TKMCheckBox = class(TKMControl)
|
||||
private
|
||||
fCaption: UnicodeString;
|
||||
fState: TKMCheckBoxState;
|
||||
fHasSemiState: Boolean;
|
||||
fFont: TKMFont;
|
||||
|
||||
function GetCheckedBool: Boolean;
|
||||
public
|
||||
DrawOutline: Boolean;
|
||||
LineColor: TColor4; //color of outline
|
||||
LineWidth: Byte;
|
||||
constructor Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; const aCaption: UnicodeString;
|
||||
aFont: TKMFont; aHasSemiState: Boolean = False); overload;
|
||||
property Caption: UnicodeString read fCaption write fCaption;
|
||||
procedure SetChecked(aChecked: Boolean);
|
||||
property Checked: Boolean read GetCheckedBool write SetChecked;
|
||||
property CheckState: TKMCheckBoxState read fState write fState;
|
||||
function IsSemiChecked: Boolean;
|
||||
procedure Check;
|
||||
procedure Uncheck;
|
||||
procedure SemiCheck;
|
||||
procedure SwitchCheck(aForward: Boolean = True);
|
||||
procedure MouseUp(X,Y: Integer; Shift: TShiftState; Button: TMouseButton); override;
|
||||
procedure Paint; override;
|
||||
end;
|
||||
|
||||
|
||||
TKMRadioGroupItem = record
|
||||
Text: UnicodeString;
|
||||
Hint: UnicodeString;
|
||||
Enabled: Boolean;
|
||||
Visible: Boolean;
|
||||
end;
|
||||
|
||||
|
||||
{ TKMRadioGroup }
|
||||
TKMRadioGroup = class(TKMControl)
|
||||
private
|
||||
fItemIndex: Integer;
|
||||
fCount: Integer;
|
||||
fItems: array of TKMRadioGroupItem;
|
||||
fFont: TKMFont;
|
||||
fMouseOverItem: SmallInt;
|
||||
fOnChange: TNotifyEvent;
|
||||
procedure UpdateMouseOverPositions(X,Y: Integer);
|
||||
function GetItem(aIndex: Integer): TKMRadioGroupItem;
|
||||
function GetItemIndexByRow(aRowIndex: Integer): Integer;
|
||||
function GetVisibleCount: Integer;
|
||||
function GetLineHeight: Single;
|
||||
function GetIsSelected: Boolean;
|
||||
protected
|
||||
function GetHint: UnicodeString; override;
|
||||
public
|
||||
AllowUncheck: Boolean; //Do we allow to uncheck selected element ? Usually not, but its possible
|
||||
DrawChkboxOutline: Boolean;
|
||||
LineColor: TColor4; //color of outline
|
||||
LineWidth: Byte;
|
||||
constructor Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; aFont: TKMFont);
|
||||
|
||||
procedure Add(const aText: String; aEnabled: Boolean = True); overload;
|
||||
procedure Add(const aText: String; aEnabled, aVisible: Boolean); overload;
|
||||
procedure Add(const aText, aHint: String; aEnabled: Boolean = True; aVisible: Boolean = True); overload;
|
||||
procedure Clear;
|
||||
property Count: Integer read fCount;
|
||||
property IsSelected: Boolean read GetIsSelected;
|
||||
property VisibleCount: Integer read GetVisibleCount;
|
||||
property ItemIndex: Integer read fItemIndex write fItemIndex;
|
||||
property OnChange: TNotifyEvent read fOnChange write fOnChange;
|
||||
property Item[aIndex: Integer]: TKMRadioGroupItem read GetItem;
|
||||
procedure SetItemEnabled(aIndex: Integer; aEnabled: Boolean; aUncheckDisabled: Boolean = True);
|
||||
procedure SetItemVisible(aIndex: Integer; aEnabled: Boolean);
|
||||
procedure CheckFirstCheckable;
|
||||
property LineHeight: Single read GetLineHeight;
|
||||
procedure MouseUp(X,Y: Integer; Shift: TShiftState; Button: TMouseButton); override;
|
||||
procedure MouseMove(X,Y: Integer; Shift: TShiftState); override;
|
||||
procedure Paint; override;
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
uses
|
||||
SysUtils, Math,
|
||||
KM_RenderUI,
|
||||
KM_Resource,
|
||||
KM_Defaults;
|
||||
|
||||
|
||||
{ TKMCheckBox }
|
||||
constructor TKMCheckBox.Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; const aCaption: UnicodeString;
|
||||
aFont: TKMFont; aHasSemiState: Boolean = False);
|
||||
begin
|
||||
inherited Create(aParent, aLeft, aTop, aWidth, aHeight);
|
||||
fFont := aFont;
|
||||
fCaption := aCaption;
|
||||
fHasSemiState := aHasSemiState;
|
||||
LineWidth := 1;
|
||||
LineColor := clChkboxOutline;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMCheckBox.SetChecked(aChecked: Boolean);
|
||||
begin
|
||||
if aChecked then
|
||||
fState := cbsChecked
|
||||
else
|
||||
fState := cbsUnchecked;
|
||||
end;
|
||||
|
||||
|
||||
function TKMCheckBox.GetCheckedBool: Boolean;
|
||||
begin
|
||||
Result := fState = cbsChecked;
|
||||
end;
|
||||
|
||||
|
||||
function TKMCheckBox.IsSemiChecked: Boolean;
|
||||
begin
|
||||
Result := fState = cbsSemiChecked;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMCheckBox.Check;
|
||||
begin
|
||||
fState := cbsChecked;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMCheckBox.SemiCheck;
|
||||
begin
|
||||
fState := cbsSemiChecked;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMCheckBox.Uncheck;
|
||||
begin
|
||||
fState := cbsUnchecked;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMCheckBox.SwitchCheck(aForward: Boolean = True);
|
||||
begin
|
||||
if fHasSemiState then
|
||||
fState := TKMCheckBoxState((Byte(fState) + 3 + 2*Byte(aForward) - 1) mod 3)
|
||||
else
|
||||
begin
|
||||
if Checked then
|
||||
Uncheck
|
||||
else
|
||||
Check;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMCheckBox.MouseUp(X,Y: Integer; Shift: TShiftState; Button: TMouseButton);
|
||||
begin
|
||||
if (csDown in State) and (Button = mbLeft) then
|
||||
case fState of
|
||||
cbsSemiChecked,
|
||||
cbsUnchecked: Check; //Let's assume we prefer check for now
|
||||
cbsChecked: Uncheck;
|
||||
end;
|
||||
inherited; //There are OnMouseUp and OnClick events there
|
||||
end;
|
||||
|
||||
|
||||
//We can replace it with something better later on. For now [x] fits just fine
|
||||
//Might need additional graphics to be added to gui.rx
|
||||
//Some kind of box with an outline, darkened background and shadow maybe, similar to other controls.
|
||||
procedure TKMCheckBox.Paint;
|
||||
var
|
||||
col, semiCol: TColor4;
|
||||
checkSize: Integer;
|
||||
begin
|
||||
inherited;
|
||||
|
||||
if Enabled then
|
||||
begin
|
||||
col := icWhite;
|
||||
semiCol := $FFCCCCCC;
|
||||
end
|
||||
else
|
||||
begin
|
||||
col := icGray2;
|
||||
semiCol := $FF888888;
|
||||
end;
|
||||
|
||||
checkSize := gRes.Fonts[fFont].GetTextSize('x').Y + 1;
|
||||
|
||||
|
||||
|
||||
TKMRenderUI.WriteBevel(AbsLeft, AbsTop, checkSize - 4, checkSize-4, 1, Byte(not IsSemiChecked and Enabled)*0.35);
|
||||
|
||||
if DrawOutline then
|
||||
TKMRenderUI.WriteOutline(AbsLeft, AbsTop, checkSize - 4, checkSize - 4, LineWidth, LineColor);
|
||||
|
||||
case fState of
|
||||
cbsChecked: TKMRenderUI.WriteText(AbsLeft + (checkSize-4) div 2, AbsTop - 1, 0, 'x', fFont, taCenter, col);
|
||||
cbsSemiChecked: TKMRenderUI.WriteText(AbsLeft + (checkSize-4) div 2, AbsTop - 1, 0, 'x', fFont, taCenter, semiCol);
|
||||
cbsUnchecked: ; //Do not draw anything
|
||||
end;
|
||||
|
||||
|
||||
TKMRenderUI.WriteText(AbsLeft + checkSize, AbsTop, Width - checkSize, fCaption, fFont, taLeft, col);
|
||||
end;
|
||||
|
||||
|
||||
{ TKMRadioGroup }
|
||||
constructor TKMRadioGroup.Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; aFont: TKMFont);
|
||||
begin
|
||||
inherited Create(aParent, aLeft, aTop, aWidth, aHeight);
|
||||
fFont := aFont;
|
||||
fItemIndex := -1;
|
||||
LineWidth := 1;
|
||||
LineColor := clChkboxOutline;
|
||||
AllowUncheck := False;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMRadioGroup.Add(const aText: String; aEnabled: Boolean);
|
||||
begin
|
||||
Add(aText, '', aEnabled);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMRadioGroup.Add(const aText: String; aEnabled, aVisible: Boolean);
|
||||
begin
|
||||
Add(aText, '', aEnabled, aVisible);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMRadioGroup.Add(const aText, aHint: String; aEnabled: Boolean = True; aVisible: Boolean = True);
|
||||
begin
|
||||
if fCount >= Length(fItems) then
|
||||
SetLength(fItems, fCount + 8);
|
||||
|
||||
fItems[fCount].Text := aText;
|
||||
fItems[fCount].Hint := aHint;
|
||||
fItems[fCount].Enabled := aEnabled;
|
||||
fItems[fCount].Visible := aVisible;
|
||||
|
||||
Inc(fCount);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMRadioGroup.Clear;
|
||||
begin
|
||||
fCount := 0;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMRadioGroup.MouseUp(X,Y: Integer; Shift: TShiftState; Button: TMouseButton);
|
||||
var
|
||||
changed: Boolean;
|
||||
begin
|
||||
if (csDown in State) and (Button = mbLeft) then
|
||||
begin
|
||||
UpdateMouseOverPositions(X,Y);
|
||||
if (fMouseOverItem <> -1) and fItems[fMouseOverItem].Enabled then
|
||||
begin
|
||||
changed := False;
|
||||
if (fMouseOverItem = fItemIndex) then
|
||||
begin
|
||||
if AllowUncheck then
|
||||
begin
|
||||
fItemIndex := -1; //Uncheck
|
||||
changed := True;
|
||||
end;
|
||||
end else begin
|
||||
fItemIndex := fMouseOverItem;
|
||||
changed := True;
|
||||
end;
|
||||
|
||||
if changed and Assigned(fOnChange) then
|
||||
begin
|
||||
fOnChange(Self);
|
||||
Exit; //Don't generate OnClick after OnChanged event (esp. when reloading Game on local change)
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
inherited; //There are OnMouseUp and OnClick events there
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMRadioGroup.MouseMove(X,Y: Integer; Shift: TShiftState);
|
||||
begin
|
||||
inherited;
|
||||
UpdateMouseOverPositions(X,Y);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMRadioGroup.UpdateMouseOverPositions(X,Y: Integer);
|
||||
var
|
||||
mouseOverRow, itemIndex: Integer;
|
||||
begin
|
||||
fMouseOverItem := -1;
|
||||
|
||||
if InRange(Y, AbsTop, AbsTop + Height) and (LineHeight > 0) then
|
||||
begin
|
||||
mouseOverRow := EnsureRange((Y - AbsTop) div Round(LineHeight), 0, VisibleCount - 1);
|
||||
itemIndex := GetItemIndexByRow(mouseOverRow);
|
||||
if (itemIndex <> -1) and InRange(X, AbsLeft, AbsLeft + LineHeight + gRes.Fonts[fFont].GetTextSize(fItems[itemIndex].Text).X) then
|
||||
fMouseOverItem := itemIndex;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
function TKMRadioGroup.GetItem(aIndex: Integer): TKMRadioGroupItem;
|
||||
begin
|
||||
Assert(aIndex < fCount, 'Can''t get radio group item for index ' + IntToStr(aIndex));
|
||||
Result := fItems[aIndex];
|
||||
end;
|
||||
|
||||
|
||||
function TKMRadioGroup.GetItemIndexByRow(aRowIndex: Integer): Integer;
|
||||
var
|
||||
I, K: Integer;
|
||||
begin
|
||||
Assert(aRowIndex < VisibleCount, 'GetItemByRow: aRowIndex >= VisibleCount');
|
||||
K := 0;
|
||||
Result := -1;
|
||||
for I := 0 to fCount - 1 do
|
||||
begin
|
||||
if not fItems[I].Visible then Continue;
|
||||
if aRowIndex = K then
|
||||
begin
|
||||
Result := I;
|
||||
Exit;
|
||||
end;
|
||||
Inc(K);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
function TKMRadioGroup.GetVisibleCount: Integer;
|
||||
var
|
||||
I: Integer;
|
||||
begin
|
||||
Result := 0;
|
||||
for I := 0 to fCount - 1 do
|
||||
if fItems[I].Visible then
|
||||
Inc(Result);
|
||||
end;
|
||||
|
||||
|
||||
function TKMRadioGroup.GetLineHeight: Single;
|
||||
begin
|
||||
Result := IfThen(VisibleCount > 0, Height / VisibleCount, Height);
|
||||
end;
|
||||
|
||||
|
||||
function TKMRadioGroup.GetIsSelected: Boolean;
|
||||
begin
|
||||
Result := fItemIndex <> -1;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMRadioGroup.SetItemEnabled(aIndex: Integer; aEnabled: Boolean; aUncheckDisabled: Boolean = True);
|
||||
begin
|
||||
Assert(aIndex < fCount, 'Can''t SetItemEnabled for index ' + IntToStr(aIndex));
|
||||
fItems[aIndex].Enabled := aEnabled;
|
||||
|
||||
// Reset item index, if this item was selected
|
||||
if aUncheckDisabled and not aEnabled and (fItemIndex = aIndex) then
|
||||
fItemIndex := -1;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMRadioGroup.SetItemVisible(aIndex: Integer; aEnabled: Boolean);
|
||||
begin
|
||||
Assert(aIndex < fCount, 'Can''t SetItemVisible for index ' + IntToStr(aIndex));
|
||||
fItems[aIndex].Visible := aEnabled;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMRadioGroup.CheckFirstCheckable;
|
||||
var
|
||||
I: Integer;
|
||||
begin
|
||||
if fItemIndex <> -1 then Exit;
|
||||
|
||||
for I := 0 to fCount - 1 do
|
||||
if fItems[I].Enabled and fItems[I].Visible then
|
||||
begin
|
||||
fItemIndex := I;
|
||||
Exit;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
function TKMRadioGroup.GetHint: UnicodeString;
|
||||
begin
|
||||
Result := inherited GetHint;
|
||||
if Result = '' then
|
||||
begin
|
||||
if fMouseOverItem <> -1 then
|
||||
Result := fItems[fMouseOverItem].Hint;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
//We can replace it with something better later on. For now [x] fits just fine
|
||||
//Might need additional graphics to be added to gui.rx
|
||||
//Some kind of box with an outline, darkened background and shadow maybe, similar to other controls.
|
||||
procedure TKMRadioGroup.Paint;
|
||||
const
|
||||
FONT_COL: array [Boolean] of TColor4 = ($FF888888, $FFFFFFFF);
|
||||
var
|
||||
CheckSize: Integer;
|
||||
I, VisibleI: Integer;
|
||||
begin
|
||||
inherited;
|
||||
if (Count = 0) or (VisibleCount = 0) then Exit; //Avoid dividing by zero
|
||||
|
||||
CheckSize := gRes.Fonts[fFont].GetTextSize('x').Y + 1;
|
||||
|
||||
VisibleI := 0;
|
||||
for I := 0 to Count - 1 do
|
||||
begin
|
||||
if not fItems[I].Visible then
|
||||
Continue;
|
||||
|
||||
TKMRenderUI.WriteBevel(AbsLeft, AbsTop + Round(VisibleI * LineHeight), CheckSize-4, CheckSize-4, 1, 0.3);
|
||||
if DrawChkboxOutline then
|
||||
TKMRenderUI.WriteOutline(AbsLeft, AbsTop + Round(VisibleI * LineHeight), CheckSize-4, CheckSize-4, LineWidth, LineColor);
|
||||
|
||||
if fItemIndex = I then
|
||||
TKMRenderUI.WriteText(AbsLeft + (CheckSize - 4) div 2, AbsTop + Round(VisibleI * LineHeight) - 1, 0,
|
||||
'x', fFont, taCenter, FONT_COL[Enabled and fItems[I].Enabled]);
|
||||
|
||||
// Caption
|
||||
TKMRenderUI.WriteText(AbsLeft + CheckSize, AbsTop + Round(VisibleI * LineHeight), Width - Round(LineHeight),
|
||||
fItems[I].Text, fFont, taLeft, FONT_COL[Enabled and fItems[I].Enabled]);
|
||||
Inc(VisibleI);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
end.
|
||||
|
||||
@@ -0,0 +1,266 @@
|
||||
unit KM_ControlsTrackBar;
|
||||
{$I KaM_Remake.inc}
|
||||
interface
|
||||
uses
|
||||
Classes, Controls,
|
||||
KM_Controls,
|
||||
KM_ResFonts,
|
||||
KM_Points;
|
||||
|
||||
type
|
||||
TKMTrackBar = class(TKMControl)
|
||||
private
|
||||
fTrackTop: Byte; //Offset trackbar from top (if Caption <> '')
|
||||
fTrackHeight: Byte; //Trackbar height
|
||||
fMinValue: Word;
|
||||
fMaxValue: Word;
|
||||
fOnChange: TNotifyEvent;
|
||||
fCaption: UnicodeString;
|
||||
fPosition: Word;
|
||||
fFont: TKMFont;
|
||||
fRange: TKMRangeInt;
|
||||
fThumbText: UnicodeString;
|
||||
fAutoThumbWidth: Boolean;
|
||||
fFixedThumbWidth: Boolean;
|
||||
procedure SetCaption(const aValue: UnicodeString);
|
||||
procedure SetPosition(aValue: Word);
|
||||
procedure SetRange(const aRange: TKMRangeInt);
|
||||
procedure UpdateThumbWidth;
|
||||
procedure SetThumbText(const Value: UnicodeString);
|
||||
procedure SetAutoThumbWidth(const aValue: Boolean);
|
||||
protected
|
||||
function DoHandleMouseWheelByDefault: Boolean; override;
|
||||
public
|
||||
Step: Byte; //Change Position by this amount each time
|
||||
ThumbWidth: Word;
|
||||
CaptionWidth: Integer;
|
||||
SliderFont: TKMFont;
|
||||
|
||||
constructor Create(aParent: TKMPanel; aLeft, aTop, aWidth: Integer; aMin, aMax: Word);
|
||||
|
||||
property Caption: UnicodeString read fCaption write SetCaption;
|
||||
property Position: Word read fPosition write SetPosition;
|
||||
property Range: TKMRangeInt read fRange write SetRange;
|
||||
property Font: TKMFont read fFont write fFont;
|
||||
property MinValue: Word read fMinValue;
|
||||
property MaxValue: Word read fMaxValue;
|
||||
property ThumbText: UnicodeString read fThumbText write SetThumbText;
|
||||
property AutoThumbWidth: Boolean read fAutoThumbWidth write SetAutoThumbWidth;
|
||||
property FixedThumbWidth: Boolean read fAutoThumbWidth write fFixedThumbWidth;
|
||||
procedure ResetRange;
|
||||
property OnChange: TNotifyEvent read fOnChange write fOnChange;
|
||||
procedure MouseDown(X,Y: Integer; Shift: TShiftState; Button: TMouseButton); override;
|
||||
procedure MouseMove(X,Y: Integer; Shift: TShiftState); override;
|
||||
procedure MouseWheel(Sender: TObject; WheelSteps: Integer; var aHandled: Boolean); override;
|
||||
procedure Paint; override;
|
||||
const
|
||||
THUMB_WIDTH_ADD = 24;
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
uses
|
||||
SysUtils, Math,
|
||||
KromUtils, KromOGLUtils,
|
||||
KM_RenderUI,
|
||||
KM_Resource, KM_ResTypes;
|
||||
|
||||
|
||||
{ TKMTrackBar }
|
||||
constructor TKMTrackBar.Create(aParent: TKMPanel; aLeft, aTop, aWidth: Integer; aMin, aMax: Word);
|
||||
begin
|
||||
inherited Create(aParent, aLeft,aTop,aWidth,0);
|
||||
Assert(aMax > aMin, 'TKMTrackBar''s minValue >= maxValue');
|
||||
fMinValue := aMin;
|
||||
fMaxValue := aMax;
|
||||
fTrackHeight := 20;
|
||||
fRange := KMRange(aMin, aMax); //set Range before position
|
||||
Position := (fMinValue + fMaxValue) div 2;
|
||||
Caption := '';
|
||||
Font := fntMetal;
|
||||
SliderFont := fntMetal;
|
||||
CaptionWidth := -1;
|
||||
|
||||
Step := 1;
|
||||
fAutoThumbWidth := False;
|
||||
fFixedThumbWidth := False;
|
||||
|
||||
UpdateThumbWidth;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMTrackBar.UpdateThumbWidth;
|
||||
begin
|
||||
if fFixedThumbWidth then Exit;
|
||||
|
||||
if AutoThumbWidth then
|
||||
ThumbWidth := Max(gRes.Fonts[SliderFont].GetTextSize(IntToStr(MaxValue)).X,
|
||||
gRes.Fonts[SliderFont].GetTextSize(ThumbText).X)
|
||||
else
|
||||
ThumbWidth := gRes.Fonts[SliderFont].GetTextSize(IntToStr(MaxValue)).X;
|
||||
|
||||
ThumbWidth := ThumbWidth + THUMB_WIDTH_ADD;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMTrackBar.SetAutoThumbWidth(const aValue: Boolean);
|
||||
begin
|
||||
fAutoThumbWidth := aValue;
|
||||
UpdateThumbWidth;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMTrackBar.SetCaption(const aValue: UnicodeString);
|
||||
begin
|
||||
fCaption := aValue;
|
||||
|
||||
if Trim(fCaption) <> '' then
|
||||
begin
|
||||
SetHeightSilently(20 + fTrackHeight);
|
||||
fTrackTop := 20;
|
||||
end
|
||||
else
|
||||
begin
|
||||
SetHeightSilently(fTrackHeight);
|
||||
fTrackTop := 0;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMTrackBar.SetPosition(aValue: Word);
|
||||
begin
|
||||
fPosition := KMEnsureRange(aValue, Range);
|
||||
ThumbText := IntToStr(fPosition);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMTrackBar.SetRange(const aRange: TKMRangeInt);
|
||||
begin
|
||||
fRange.Min := EnsureRange(aRange.Min, MinValue, MaxValue);
|
||||
fRange.Max := EnsureRange(aRange.Max, MinValue, MaxValue);
|
||||
Position := fPosition; //Update position due to range change
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMTrackBar.SetThumbText(const Value: UnicodeString);
|
||||
begin
|
||||
fThumbText := Value;
|
||||
UpdateThumbWidth;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMTrackBar.ResetRange;
|
||||
begin
|
||||
fRange.Min := MinValue;
|
||||
fRange.Max := MaxValue;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMTrackBar.MouseDown(X,Y: Integer; Shift: TShiftState; Button: TMouseButton);
|
||||
begin
|
||||
inherited;
|
||||
MouseMove(X, Y, Shift);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMTrackBar.MouseMove(X,Y: Integer; Shift: TShiftState);
|
||||
var
|
||||
newPos: Integer;
|
||||
posX, stepX: Single;
|
||||
begin
|
||||
inherited;
|
||||
|
||||
newPos := Position;
|
||||
if (ssLeft in Shift)
|
||||
and InRange(Y - AbsTop - fTrackTop, 0, fTrackHeight) then
|
||||
begin
|
||||
posX := 0;
|
||||
if AutoThumbWidth then
|
||||
begin
|
||||
stepX := (Width - ThumbWidth) / (fMaxValue - fMinValue) / Step; // width between marks
|
||||
posX := stepX * (Position / Step) + (ThumbWidth div 2); // actual marks positions on track
|
||||
end;
|
||||
if not AutoThumbWidth
|
||||
or not InRange(X - AbsLeft, posX - ThumbWidth div 2, posX + ThumbWidth div 2) then // do not update pos, if we hover over thumb
|
||||
newPos := EnsureRange(fMinValue + Round(((X-AbsLeft-ThumbWidth div 2) / (Width - ThumbWidth - 4))*(fMaxValue - fMinValue)/Step)*Step, fMinValue, fMaxValue);
|
||||
end;
|
||||
|
||||
if newPos <> Position then
|
||||
begin
|
||||
Position := newPos;
|
||||
|
||||
if Assigned(fOnChange) then
|
||||
fOnChange(Self);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
function TKMTrackBar.DoHandleMouseWheelByDefault: Boolean;
|
||||
begin
|
||||
Result := False;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMTrackBar.MouseWheel(Sender: TObject; WheelSteps: Integer; var aHandled: Boolean);
|
||||
var
|
||||
newPos: Integer;
|
||||
begin
|
||||
inherited;
|
||||
|
||||
if aHandled or (WheelSteps = 0) then Exit;
|
||||
|
||||
aHandled := WheelSteps <> 0;
|
||||
|
||||
Focus;
|
||||
|
||||
newPos := Position;
|
||||
|
||||
if WheelSteps <> 0 then
|
||||
newPos := EnsureRange(newPos - Step*fMouseWheelStep*WheelSteps, fMinValue, fMaxValue);
|
||||
|
||||
if newPos <> Position then
|
||||
begin
|
||||
Position := newPos;
|
||||
|
||||
if Assigned(fOnChange) then
|
||||
fOnChange(Self);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMTrackBar.Paint;
|
||||
const
|
||||
TEXT_COLOR: array [Boolean] of TColor4 = ($FF888888, $FFFFFFFF);
|
||||
var
|
||||
thumbPos, thumbHeight, rangeMinPos, rangeMaxPos: Word;
|
||||
capWidth: Integer;
|
||||
begin
|
||||
inherited;
|
||||
|
||||
if fCaption <> '' then
|
||||
begin
|
||||
if CaptionWidth = -1 then
|
||||
capWidth := Width
|
||||
else
|
||||
capWidth := CaptionWidth;
|
||||
|
||||
TKMRenderUI.WriteText(AbsLeft, AbsTop, capWidth, fCaption, fFont, taLeft, TEXT_COLOR[Enabled]);
|
||||
end;
|
||||
|
||||
rangeMinPos := Round(Width*(fRange.Min-fMinValue) / (fMaxValue - fMinValue));
|
||||
rangeMaxPos := Round(Width*(fRange.Max-fMinValue) / (fMaxValue - fMinValue));
|
||||
|
||||
TKMRenderUI.WriteBevel(AbsLeft, AbsTop+fTrackTop+1, rangeMinPos, fTrackHeight-2, 0, 0.3);
|
||||
TKMRenderUI.WriteBevel(AbsLeft + rangeMinPos, AbsTop+fTrackTop+2, rangeMaxPos - rangeMinPos, fTrackHeight-4);
|
||||
TKMRenderUI.WriteBevel(AbsLeft + rangeMaxPos, AbsTop+fTrackTop+1, Width - rangeMaxPos, fTrackHeight-2, 0, 0.3);
|
||||
|
||||
thumbPos := Round(Mix (0, Width - ThumbWidth, 1-(Position-fMinValue) / (fMaxValue - fMinValue)));
|
||||
thumbHeight := gRes.Sprites[rxGui].RXData.Size[132].Y;
|
||||
|
||||
TKMRenderUI.WritePicture(AbsLeft + thumbPos, AbsTop+fTrackTop, ThumbWidth, thumbHeight, [anLeft,anRight], rxGui, 132);
|
||||
TKMRenderUI.WriteText(AbsLeft + thumbPos + ThumbWidth div 2, AbsTop+fTrackTop+3, 0, ThumbText, SliderFont, taCenter, TEXT_COLOR[Enabled]);
|
||||
end;
|
||||
|
||||
|
||||
end.
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
unit KM_ControlsTypes;
|
||||
{$I KaM_Remake.inc}
|
||||
interface
|
||||
|
||||
|
||||
type
|
||||
TKMAllowedChars = (
|
||||
acDigits, // Only 0..9 digits, for numeric input
|
||||
acHex, // Only 0..9 A..F a..f, for input hex values (colors)
|
||||
acANSI7, // #33..#123,#125,#126 - only basic latin chars and symbols for user nickname, except |
|
||||
acFileName, // Exclude symbols that can't be used in filenames
|
||||
acText, // Anything is allowed except for eol symbol
|
||||
acAll // Anything is allowed
|
||||
);
|
||||
|
||||
const
|
||||
CTRL_HIGHLIGHT_COEF_DEF = 0.4;
|
||||
|
||||
|
||||
implementation
|
||||
|
||||
|
||||
end.
|
||||
@@ -0,0 +1,62 @@
|
||||
unit KM_ControlsUtils;
|
||||
{$I KaM_Remake.inc}
|
||||
interface
|
||||
uses
|
||||
Classes,
|
||||
KM_ControlsTypes,
|
||||
KM_CommonTypes;
|
||||
|
||||
|
||||
// Check if specified aChar is allowed for specified aAllowedChars type
|
||||
function IsCharAllowed(aChar: WideChar; aAllowedChars: TKMAllowedChars): Boolean;
|
||||
|
||||
function IsSelecting(Key: Word; Shift: TShiftState): Boolean;
|
||||
|
||||
function GetCursorDir(aKey: Word): TKMCursorDir;
|
||||
|
||||
|
||||
implementation
|
||||
uses
|
||||
{$IFDEF MSWindows} Windows, {$ENDIF}
|
||||
{$IFDEF Unix} LCLIntf, LCLType, {$ENDIF}
|
||||
Math,
|
||||
KromUtils;
|
||||
|
||||
|
||||
// Check if specified aChar is allowed for specified aAllowedChars type
|
||||
function IsCharAllowed(aChar: WideChar; aAllowedChars: TKMAllowedChars): Boolean;
|
||||
const
|
||||
HEX_DIGITS: TSetOfAnsiChar = [#48..#57, #65..#70, #97..#102]; // 0..9 A..F a..f
|
||||
ANSI7_CHARS: TSetOfAnsiChar = [#32..#123, #125..#126]; // except | character
|
||||
NON_FILE_CHARS: TSetOfAnsiChar = [#0 .. #31, '<', '>', #176, '|', '"', '\', '/', ':', '*', '?'];
|
||||
NON_TEXT_CHARS_WEOL: TSetOfAnsiChar = [#0 .. #31, #176, '|']; // ° has negative width so acts like a backspace in KaM fonts
|
||||
NON_TEXT_CHARS: TSetOfAnsiChar = [#0 .. #31, #176]; // ° has negative width so acts like a backspace in KaM fonts
|
||||
begin
|
||||
Result := not ((aAllowedChars = acDigits) and not InRange(Ord(aChar), 48, 57)
|
||||
or (aAllowedChars = acHex) and not CharInSet(aChar, HEX_DIGITS)
|
||||
or (aAllowedChars = acANSI7) and not CharInSet(aChar, ANSI7_CHARS)
|
||||
or (aAllowedChars = acFileName) and CharInSet(aChar, NON_FILE_CHARS)
|
||||
or (aAllowedChars = acText) and CharInSet(aChar, NON_TEXT_CHARS_WEOL)
|
||||
or (aAllowedChars = acAll) and CharInSet(aChar, NON_TEXT_CHARS));
|
||||
end;
|
||||
|
||||
|
||||
function IsSelecting(Key: Word; Shift: TShiftState): Boolean;
|
||||
begin
|
||||
Result := (ssShift in Shift) and (Key <> VK_SHIFT);
|
||||
end;
|
||||
|
||||
|
||||
function GetCursorDir(aKey: Word): TKMCursorDir;
|
||||
begin
|
||||
case aKey of
|
||||
VK_LEFT: Result := cdBack;
|
||||
VK_RIGHT: Result := cdForward;
|
||||
else
|
||||
Result := cdNone;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
end.
|
||||
|
||||
@@ -0,0 +1,309 @@
|
||||
unit KM_ControlsWaresRow;
|
||||
{$I KaM_Remake.inc}
|
||||
interface
|
||||
uses
|
||||
Classes, Controls,
|
||||
KM_Controls, KM_ControlsBase,
|
||||
KM_ResTypes,
|
||||
KM_CommonTypes, KM_Defaults;
|
||||
|
||||
|
||||
type
|
||||
// Row with resource name and icons
|
||||
TKMWaresRow = class(TKMButtonFlatCommon)
|
||||
public
|
||||
WareCount: Word;
|
||||
WareCntAsNumber: Boolean;
|
||||
constructor Create(aParent: TKMPanel; aLeft, aTop, aWidth: Integer); overload;
|
||||
constructor Create(aParent: TKMPanel; aLeft, aTop, aWidth: Integer; aClickable: Boolean); overload;
|
||||
procedure Paint; override;
|
||||
end;
|
||||
|
||||
|
||||
// Ware order bar
|
||||
TKMWareOrderRow = class(TKMControl)
|
||||
private
|
||||
fWaresRow: TKMWaresRow;
|
||||
fOrderAdd: TKMButton;
|
||||
fOrderLab: TKMLabel;
|
||||
fOrderRem: TKMButton;
|
||||
fOrderCount: Integer;
|
||||
fImmediateOrder: Boolean; // Order count can be changed immediately in control. Always set False because of MP delay
|
||||
procedure ButtonClick(Sender: TObject; Shift: TShiftState);
|
||||
procedure ClickHold(Sender: TObject; Button: TMouseButton; var aHandled: Boolean);
|
||||
procedure SetOrderRemHint(const aValue: UnicodeString);
|
||||
procedure SetOrderAddHint(const aValue: UnicodeString);
|
||||
procedure SetOrderCount(aValue: Integer);
|
||||
protected
|
||||
procedure SetTop(aValue: Integer); override;
|
||||
procedure SetEnabled(aValue: Boolean); override;
|
||||
procedure SetVisible(aValue: Boolean); override;
|
||||
function DoHandleMouseWheelByDefault: Boolean; override;
|
||||
public
|
||||
OrderCntMax: Integer;
|
||||
OnChange: TKMNotifyEventInteger;
|
||||
constructor Create(aParent: TKMPanel; aLeft, aTop, aWidth: Integer; aOrderCntMax: Integer = MAX_WARES_IN_HOUSE);
|
||||
property WareRow: TKMWaresRow read fWaresRow;
|
||||
property OrderCount: Integer read fOrderCount write SetOrderCount;
|
||||
property OrderRemHint: UnicodeString write SetOrderRemHint;
|
||||
property OrderAddHint: UnicodeString write SetOrderAddHint;
|
||||
procedure MouseWheel(Sender: TObject; WheelSteps: Integer; var aHandled: Boolean); override;
|
||||
procedure Paint; override;
|
||||
end;
|
||||
|
||||
|
||||
// Production cost bar
|
||||
TKMCostsRow = class(TKMControl)
|
||||
public
|
||||
RX: TRXType;
|
||||
TexID1, TexID2: Word;
|
||||
Count: Byte;
|
||||
Caption: UnicodeString;
|
||||
MaxCount: Byte;
|
||||
constructor Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; aMaxCount: Byte = 6);
|
||||
procedure Paint; override;
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
uses
|
||||
{$IFDEF MSWindows} Windows, {$ENDIF}
|
||||
{$IFDEF Unix} LCLIntf, LCLType, {$ENDIF}
|
||||
SysUtils, Math,
|
||||
KM_RenderUI,
|
||||
KM_ResFonts,
|
||||
KM_UtilsExt;
|
||||
|
||||
const
|
||||
WARE_ROW_HEIGHT = 21;
|
||||
|
||||
|
||||
{ TKMWaresRow }
|
||||
constructor TKMWaresRow.Create(aParent: TKMPanel; aLeft, aTop, aWidth: Integer);
|
||||
begin
|
||||
Create(aParent, aLeft, aTop, aWidth, False);
|
||||
end;
|
||||
|
||||
|
||||
constructor TKMWaresRow.Create(aParent: TKMPanel; aLeft, aTop, aWidth: Integer; aClickable: Boolean);
|
||||
begin
|
||||
inherited Create(aParent, aLeft, aTop, aWidth, WARE_ROW_HEIGHT, 0);
|
||||
Clickable := aClickable;
|
||||
HideHighlight := not aClickable;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMWaresRow.Paint;
|
||||
var
|
||||
I: Integer;
|
||||
begin
|
||||
inherited;
|
||||
TKMRenderUI.WriteText(AbsLeft + 4, AbsTop + 3, Width-8, Caption, fntGame, taLeft, $FFE0E0E0);
|
||||
//Render in reverse order so the rightmost resource is on top (otherwise lighting looks wrong)
|
||||
if WareCntAsNumber then
|
||||
begin
|
||||
TKMRenderUI.WriteText(AbsLeft + Width - 18 - 70 + 24, AbsTop + 3, 40, IntToStr(WareCount), fntGame, taRight, $FFE0E0E0);
|
||||
TKMRenderUI.WritePicture(AbsLeft + Width - 18, AbsTop + 3, 14, 14, [], RX, TexID);
|
||||
end else
|
||||
for I := WareCount - 1 downto 0 do
|
||||
TKMRenderUI.WritePicture(AbsLeft + Width - 18 - I * 14, AbsTop + 3, 14, 14, [], RX, TexID);
|
||||
end;
|
||||
|
||||
|
||||
{ TKMWareOrderRow }
|
||||
constructor TKMWareOrderRow.Create(aParent: TKMPanel; aLeft, aTop, aWidth: Integer; aOrderCntMax: Integer = MAX_WARES_IN_HOUSE);
|
||||
begin
|
||||
inherited Create(aParent, aLeft, aTop, aWidth, WARE_ROW_HEIGHT);
|
||||
|
||||
fWaresRow := TKMWaresRow.Create(aParent, aLeft + 68, aTop, aWidth - 68);
|
||||
|
||||
fImmediateOrder := False;
|
||||
|
||||
OrderCntMax := aOrderCntMax;
|
||||
|
||||
fOrderRem := TKMButton.Create(aParent, aLeft, 0, 20, Height - 2, '-', bsGame);
|
||||
fOrderAdd := TKMButton.Create(aParent, aLeft + 46, 0, 20, Height - 2, '+', bsGame);
|
||||
//Label after buttons, to be sure it will be on top of them, to let player read value from it, if too long (more then 3 symbols)
|
||||
fOrderLab := TKMLabel.Create (aParent, aLeft + 33, 0, '', fntGrey, taCenter);
|
||||
|
||||
fOrderAdd.CapOffsetY := 1;
|
||||
|
||||
fOrderRem.OnClickShift := ButtonClick;
|
||||
fOrderAdd.OnClickShift := ButtonClick;
|
||||
fOrderRem.OnMouseWheel := MouseWheel;
|
||||
fOrderAdd.OnMouseWheel := MouseWheel;
|
||||
fWaresRow.OnMouseWheel := MouseWheel;
|
||||
fOrderRem.OnClickHold := ClickHold;
|
||||
fOrderAdd.OnClickHold := ClickHold;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMWareOrderRow.ButtonClick(Sender: TObject; Shift: TShiftState);
|
||||
var
|
||||
amt: Integer;
|
||||
begin
|
||||
amt := GetMultiplicator(Shift);
|
||||
if Sender = fOrderRem then
|
||||
amt := -amt;
|
||||
|
||||
if amt = 0 then Exit;
|
||||
|
||||
if fImmediateOrder then
|
||||
OrderCount := fOrderCount + amt;
|
||||
|
||||
Focus;
|
||||
|
||||
if Assigned(OnChange) then
|
||||
OnChange(Self, amt);
|
||||
end;
|
||||
|
||||
|
||||
function TKMWareOrderRow.DoHandleMouseWheelByDefault: Boolean;
|
||||
begin
|
||||
Result := False;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMWareOrderRow.MouseWheel(Sender: TObject; WheelSteps: Integer; var aHandled: Boolean);
|
||||
var
|
||||
amt: Integer;
|
||||
begin
|
||||
inherited;
|
||||
|
||||
if aHandled then Exit;
|
||||
|
||||
amt := MouseWheelStep * WheelSteps;
|
||||
if GetKeyState(VK_SHIFT) < 0 then
|
||||
amt := amt * 10;
|
||||
|
||||
if fImmediateOrder then
|
||||
OrderCount := fOrderCount + amt;
|
||||
|
||||
aHandled := amt <> 0;
|
||||
|
||||
Focus;
|
||||
|
||||
if Assigned(OnChange) then
|
||||
OnChange(Self, amt);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMWareOrderRow.ClickHold(Sender: TObject; Button: TMouseButton; var aHandled: Boolean);
|
||||
var
|
||||
amt: Integer;
|
||||
begin
|
||||
inherited;
|
||||
aHandled := True;
|
||||
|
||||
amt := GetMultiplicator(Button);
|
||||
|
||||
if Sender = fOrderRem then
|
||||
amt := -amt;
|
||||
|
||||
if amt = 0 then Exit;
|
||||
|
||||
if fImmediateOrder then
|
||||
OrderCount := fOrderCount + amt;
|
||||
|
||||
if Assigned(OnChange) then
|
||||
OnChange(Self, amt);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMWareOrderRow.SetOrderCount(aValue: Integer);
|
||||
begin
|
||||
fOrderCount := EnsureRange(aValue, 0, OrderCntMax);
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMWareOrderRow.SetOrderRemHint(const aValue: UnicodeString);
|
||||
begin
|
||||
fOrderRem.Hint := aValue;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMWareOrderRow.SetOrderAddHint(const aValue: UnicodeString);
|
||||
begin
|
||||
fOrderAdd.Hint := aValue;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMWareOrderRow.SetTop(aValue: Integer);
|
||||
begin
|
||||
inherited;
|
||||
fWaresRow.Top := aValue;
|
||||
end;
|
||||
|
||||
|
||||
//Copy property to buttons
|
||||
procedure TKMWareOrderRow.SetEnabled(aValue: Boolean);
|
||||
begin
|
||||
inherited;
|
||||
fWaresRow.Enabled := Enabled;
|
||||
fOrderRem.Enabled := Enabled;
|
||||
fOrderLab.Enabled := Enabled;
|
||||
fOrderAdd.Enabled := Enabled;
|
||||
end;
|
||||
|
||||
|
||||
//Copy property to buttons. Otherwise they won't be rendered
|
||||
procedure TKMWareOrderRow.SetVisible(aValue: Boolean);
|
||||
begin
|
||||
inherited;
|
||||
fWaresRow.Visible := IsSetVisible;
|
||||
fOrderRem.Visible := IsSetVisible;
|
||||
fOrderLab.Visible := IsSetVisible;
|
||||
fOrderAdd.Visible := IsSetVisible;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMWareOrderRow.Paint;
|
||||
begin
|
||||
inherited;
|
||||
|
||||
fOrderRem.Top := Top + 1; //Use internal fTop instead of GetTop (which will return absolute value)
|
||||
fOrderLab.Top := Top + 4;
|
||||
fOrderAdd.Top := Top + 1;
|
||||
|
||||
fOrderLab.Caption := IntToKStr(OrderCount, 1000);
|
||||
end;
|
||||
|
||||
|
||||
{ TKMCostsRow }
|
||||
constructor TKMCostsRow.Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; aMaxCount: Byte = 6);
|
||||
begin
|
||||
inherited Create(aParent, aLeft, aTop, aWidth, aHeight);
|
||||
|
||||
MaxCount := aMaxCount;
|
||||
end;
|
||||
|
||||
|
||||
procedure TKMCostsRow.Paint;
|
||||
var
|
||||
I, gap: Integer;
|
||||
begin
|
||||
inherited;
|
||||
TKMRenderUI.WriteText(AbsLeft, AbsTop + 4, Width-20, Caption, fntGrey, taLeft, $FFFFFFFF);
|
||||
|
||||
if Count > 0 then
|
||||
begin
|
||||
if Count <= MaxCount then
|
||||
gap := 20
|
||||
else
|
||||
gap := Trunc(MaxCount * 20 / Count);
|
||||
|
||||
if TexID1 <> 0 then
|
||||
for I := Count - 1 downto 0 do
|
||||
TKMRenderUI.WritePicture(AbsLeft+Width-gap*(I+1), AbsTop, 20, Height, [], RX, TexID1);
|
||||
end else
|
||||
begin
|
||||
if TexID1 <> 0 then
|
||||
TKMRenderUI.WritePicture(AbsLeft+Width-40, AbsTop, 20, Height, [], RX, TexID1);
|
||||
if TexID2 <> 0 then
|
||||
TKMRenderUI.WritePicture(AbsLeft+Width-20, AbsTop, 20, Height, [], RX, TexID2);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
end.
|
||||
|
||||
Reference in new issue
Block a user