anchor.diff:  end;
   
   
  -{ TKMNumericEdit }
  -constructor TKMNumericEdit.Create(aParent: TKMPanel; aLeft, aTop: Integer; aValueMin, aValueMax: SmallInt; aFont: TKMFont = fnt_Grey; aSelectable: Boolean = True);
  -var
  -  W: Word;
  -begin
  -  // Text width + padding + buttons
  -  W := gRes.Fonts[fnt_Grey].GetTextSize(IntToStr(aValueMax)).X + 10 + 20 + 20;
  -
  -  inherited Create(aParent, aLeft, aTop, W, 20, aFont, aSelectable);
  -
  -  ReadOnly := False;
  -
  -  ValueMin := aValueMin;
  -  ValueMax := aValueMax;
  -  Value := 0;
  -
  -  fButtonDec := TKMButton.Create(aParent, aLeft,           aTop, 20, 20, '-', bsGame);
  -  fButtonInc := TKMButton.Create(aParent, aLeft + W - 20,  aTop, 20, 20, '+', bsGame);
  -  fButtonDec.OnClickShift := ButtonClick;
  -  fButtonInc.OnClickShift := ButtonClick;
  -  fButtonDec.OnMouseWheel := MouseWheel;
  -  fButtonInc.OnMouseWheel := MouseWheel;
  -  fButtonDec.OnClickHold := ClickHold;
  -  fButtonInc.OnClickHold := ClickHold;
  -end;
  -
  -
  -procedure TKMNumericEdit.ClickHold(Sender: TObject; Button: TMouseButton; var aHandled: Boolean);
  -var Amt: Integer;
  -    Shift: TShiftState;
  -begin
  -  inherited;
  -  aHandled := True;
  -  Shift := [];
  -  case Button of
  -    mbLeft:   Include(Shift, ssLeft);
  -    mbRight:  Include(Shift, ssRight);
  -  end;
  -
  -  if GetKeyState(VK_SHIFT) < 0 then
  -    Include(Shift, ssShift);
  -
  -  Amt := GetMultiplicator(Shift);
  -
  -  if Sender = fButtonDec then
  -    Value := Value - Amt
  -  else
  -  if Sender = fButtonInc then
  -    Value := Value + Amt;
  -
  -  if (Amt <> 0) and Assigned(OnChange) then
  -    OnChange(Self);
  -end;
  -
  -
  -function TKMNumericEdit.KeyDown(Key: Word; Shift: TShiftState): Boolean;
  -begin
  -  Result := KeyEventHandled(Key, Shift);
  -  inherited KeyDown(Key, Shift);
  -
  -  case Key of
  -    VK_UP:      Value := Value + 1 + 9*Byte(ssShift in Shift);
  -    VK_DOWN:    Value := Value - 1 - 9*Byte(ssShift in Shift);
  -  end;
  -end;
  -
  -
  -function TKMNumericEdit.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_LEFT,
  -    VK_RIGHT,
  -    VK_HOME,
  -    VK_END: Result := (fText <> ''); //These keys have no effect when text is blank
  -  end;
  -
  -  //We want these keys to be ignored by TKMNumericEdit
  -  if Key in [VK_F1..VK_F12, VK_ESCAPE, VK_RETURN, VK_TAB] 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 [Ord('A'), Ord('C'), Ord('X'), Ord('V')]);
  -end;
  -
  -
  -function TKMNumericEdit.GetMaxLength: Word;
  -begin
  -  if Max(Abs(ValueMax), Abs(ValueMin)) <> 0 then
  -    Result := Trunc(Log10(Max(Abs(ValueMax), Abs(ValueMin)))) + 1
  -  else
  -    Result := 1;
  -end;
  -
  -
  -procedure TKMNumericEdit.MouseWheel(Sender: TObject; WheelDelta: Integer);
  -begin
  -  inherited;
  -
  -  if WheelDelta > 0 then Value := Value + 1 + 9*Byte(GetKeyState(VK_SHIFT) < 0);
  -  if WheelDelta < 0 then Value := Value - 1 - 9*Byte(GetKeyState(VK_SHIFT) < 0);
  -
  -  Focus;
  -
  -  if Assigned(OnChange) then
  -    OnChange(Self);
  -end;
  -
  -
  -procedure TKMNumericEdit.ButtonClick(Sender: TObject; Shift: TShiftState);
  -begin
  -  if Sender = fButtonDec then
  -    Value := Value - GetMultiplicator(Shift)
  -  else
  -  if Sender = fButtonInc then
  -    Value := Value + GetMultiplicator(Shift)
  -  else
  -    Exit;
  -
  -  Focus;
  -
  -  if Assigned(OnChange) then
  -    OnChange(Self);
  -end;
  -
  -
  -procedure TKMNumericEdit.SetSharedHint(aHint: UnicodeString);
  -begin
  -  Hint := aHint;
  -  fButtonInc.Hint := aHint;
  -  fButtonDec.Hint := aHint;
  -end;
  -
  -
  -procedure TKMNumericEdit.SetTop(aValue: Integer);
  -begin
  -  inherited;
  -
  -  fButtonDec.Top := Top;
  -  fButtonInc.Top := Top;
  -end;
  -
  -
  -procedure TKMNumericEdit.SetLeft(aValue: Integer);
  -begin
  -  inherited;
  -
  -  fButtonDec.Left := Left;
  -  fButtonInc.Top := Left + Width - 20;
  -end;
  -
  -
  -procedure TKMNumericEdit.SetEnabled(aValue: Boolean);
  -begin
  -  inherited;
  -  fButtonDec.Enabled := fEnabled;
  -  fButtonInc.Enabled := fEnabled;
  -end;
  -
  -
  -procedure TKMNumericEdit.SetVisible(aValue: Boolean);
  -begin
  -  inherited;
  -  fButtonDec.Visible := fVisible;
  -  fButtonInc.Visible := fVisible;
  -end;
  -
  -
  -function TKMNumericEdit.GetSelfAbsLeft: Integer;
  -begin
  -  Result := AbsLeft + 20; // Need to add extra +1 because of left button border
  -end;
  -
  -
  -function TKMNumericEdit.GetSelfWidth: Integer;
  -begin
  -  Result := fWidth - 40; // Need to substruct extra -2 because of 2 buttons borders
  -end;
  -
  -
  -function TKMNumericEdit.IsCharValid(Key: WideChar): Boolean;
  -begin
  -  Result := SysUtils.CharInSet(Key, ['0'..'9']);
  -end;
  -
  -
  -procedure TKMNumericEdit.SetValue(aValue: Integer);
  -begin
  -  fValue := EnsureRange(aValue, ValueMin, ValueMax);
  -  fText := IntToStr(fValue);
  -
  -  //External Value assignment should not generate OnChange event
  -end;
  -
  -
  -procedure TKMNumericEdit.ValidateText;
  -var
  -  I: Integer;
  -begin
  -  //Validate contents
  -  for I := Length(fText) downto 1 do
  -  if not KromUtils.CharInSet(fText[I], ['0'..'9']) then
  -  begin
  -    Delete(fText, I, 1);
  -    if CursorPos >= I then //Keep cursor in place
  -      CursorPos := CursorPos - 1;
  -  end;
  -
  -  if fText = '' then
  -    Value := 0
  -  else
  -    Value := StrToInt(fText);
  -
  -  if Assigned(OnChange) then
  -    OnChange(Self);
  -end;
  -
  -
  -procedure TKMNumericEdit.Paint;
  -var
  -  Col: TColor4;
  -  RText: UnicodeString;
  -  OffX: Integer;
  -begin
  -  inherited;
  -
  -  TKMRenderUI.WriteBevel(AbsLeft + 20, AbsTop, Width - 40, Height);
  -  if fEnabled then Col:=$FFFFFFFF else Col:=$FF888888;
  -
  -  RText := Copy(fText, fLeftIndex+1, Length(fText)); //Remove characters to the left of fLeftIndex
  -
  -  PaintSelection;
  -
  -  TKMRenderUI.WriteText(AbsLeft+24, AbsTop+3, 0, fText, fFont, taLeft, Col); //Characters that do not fit are trimmed
  -
  -  //Render text cursor
  -  if (csFocus in State) and ((TimeGet div 500) mod 2 = 0) then
  -  begin
  -    SetLength(RText, CursorPos - fLeftIndex);
  -    OffX := AbsLeft + 22 + gRes.Fonts[fFont].GetTextSize(RText).X;
  -    TKMRenderUI.WriteShape(OffX, AbsTop+2, 3, Height-4, Col, $FF000000);
  -  end;
  -end;
  -
  -
   { TKMCheckBox }
   constructor TKMCheckBox.Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; aCaption: UnicodeString; aFont: TKMFont);
   begin
  
positive.code:     procedure MouseWheel(Sender: TObject; WheelDelta: Integer); override;
      procedure Paint; override;
    end;
  
  
    { Checkbox }
    TKMCheckBox = class(TKMControl)
    private
      fCaption: UnicodeString;
      fChecked: Boolean;
      fFont: TKMFont;
    public
      constructor Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; aCaption: UnicodeString; aFont: TKMFont); overload;
      property Caption: UnicodeString read fCaption write fCaption;
      property Checked: Boolean read fChecked write fChecked;
      procedure MouseUp(X,Y: Integer; Shift: TShiftState; Button: TMouseButton); override;
      procedure Paint; override;
    end;
  
  
    { TKMRadioGroup }
    TKMRadioGroup = class(TKMControl)
    private
      fItemIndex: Integer;
      fCount: Integer;
      fItems: array of record
        Text: UnicodeString;
        Enabled: Boolean;
      end;
      fFont: TKMFont;
      fOnChange: TNotifyEvent;
    public
      constructor Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; aFont: TKMFont);
  
      procedure Add(aText: UnicodeString; aEnabled: Boolean = True);
      procedure Clear;
      property Count: Integer read fCount;
      property ItemIndex: Integer read fItemIndex write fItemIndex;
      property OnChange: TNotifyEvent read fOnChange write fOnChange;
      procedure MouseUp(X,Y: Integer; Shift: TShiftState; Button: TMouseButton); override;
      procedure Paint; override;
    end;
  
  
    {Percent bar}
    TKMPercentBar = class(TKMControl)
    private
      fFont: TKMFont;
      fPosition: Single;
      fTextAlign: TKMTextAlign;
      fSeam: Single;
      procedure SetPosition(aValue: Single);
      procedure SetSeam(aValue: Single);
    public
      Caption: UnicodeString;
      FontColor: TColor4;
      TextYOffset: Integer;
      constructor Create(aParent: TKMPanel; aLeft, aTop, aWidth, aHeight: Integer; aFont: TKMFont = fnt_Mini);
      property Seam: Single read fSeam write SetSeam;
      property Position: Single read fPosition write SetPosition;
      procedure Paint; override;
    end;
  
  
    {Row with resource name and icons}
    TKMWaresRow = class(TKMControl)
    public
      RX: TRXType;
      TexID: Word;
      Caption: UnicodeString;
      WareCount: Byte;
      constructor Create(aParent: TKMPanel; aLeft, aTop, aWidth: Integer);
      procedure Paint; override;
    end;
  
  
    {Ware order bar}
    TKMWareOrderRow = class(TKMWaresRow)
  
negative.code:   //Render in reverse order so the rightmost resource is on top (otherwise lighting looks wrong)
    for I := WareCount - 1 downto 0 do
      TKMRenderUI.WritePicture(AbsLeft + Width - 18 - I * 14, AbsTop + 3, 14, 14, [], RX, TexID);
  end;
  
  
  { TKMNumericEdit }
  constructor TKMNumericEdit.Create(aParent: TKMPanel; aLeft, aTop: Integer; aValueMin, aValueMax: SmallInt; aFont: TKMFont = fnt_Grey; aSelectable: Boolean = True);
  var
    W: Word;
  begin
    // Text width + padding + buttons
    W := gRes.Fonts[fnt_Grey].GetTextSize(IntToStr(aValueMax)).X + 10 + 20 + 20;
  
    inherited Create(aParent, aLeft, aTop, W, 20, aFont, aSelectable);
  
    ReadOnly := False;
  
    ValueMin := aValueMin;
    ValueMax := aValueMax;
    Value := 0;
  
    fButtonDec := TKMButton.Create(aParent, aLeft,           aTop, 20, 20, '-', bsGame);
    fButtonInc := TKMButton.Create(aParent, aLeft + W - 20,  aTop, 20, 20, '+', bsGame);
    fButtonDec.OnClickShift := ButtonClick;
    fButtonInc.OnClickShift := ButtonClick;
    fButtonDec.OnMouseWheel := MouseWheel;
    fButtonInc.OnMouseWheel := MouseWheel;
    fButtonDec.OnClickHold := ClickHold;
    fButtonInc.OnClickHold := ClickHold;
  end;
  
  
  procedure TKMNumericEdit.ClickHold(Sender: TObject; Button: TMouseButton; var aHandled: Boolean);
  var Amt: Integer;
      Shift: TShiftState;
  begin
    inherited;
    aHandled := True;
    Shift := [];
    case Button of
      mbLeft:   Include(Shift, ssLeft);
      mbRight:  Include(Shift, ssRight);
    end;
  
    if GetKeyState(VK_SHIFT) < 0 then
      Include(Shift, ssShift);
  
    Amt := GetMultiplicator(Shift);
  
    if Sender = fButtonDec then
      Value := Value - Amt
    else
    if Sender = fButtonInc then
      Value := Value + Amt;
  
    if (Amt <> 0) and Assigned(OnChange) then
      OnChange(Self);
  end;
  
  
  function TKMNumericEdit.KeyDown(Key: Word; Shift: TShiftState): Boolean;
  begin
    Result := KeyEventHandled(Key, Shift);
    inherited KeyDown(Key, Shift);
  
    case Key of
      VK_UP:      Value := Value + 1 + 9*Byte(ssShift in Shift);
      VK_DOWN:    Value := Value - 1 - 9*Byte(ssShift in Shift);
    end;
  end;
  
  
  function TKMNumericEdit.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_LEFT,
      VK_RIGHT,
      VK_HOME,
      VK_END: Result := (fText <> ''); //These keys have no effect when text is blank
    end;
  
    //We want these keys to be ignored by TKMNumericEdit
    if Key in [VK_F1..VK_F12, VK_ESCAPE, VK_RETURN, VK_TAB] 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 [Ord('A'), Ord('C'), Ord('X'), Ord('V')]);
  end;
  
  
  function TKMNumericEdit.GetMaxLength: Word;
  begin
    if Max(Abs(ValueMax), Abs(ValueMin)) <> 0 then
      Result := Trunc(Log10(Max(Abs(ValueMax), Abs(ValueMin)))) + 1
    else
      Result := 1;
  end;
  
  
  procedure TKMNumericEdit.MouseWheel(Sender: TObject; WheelDelta: Integer);
  begin
    inherited;
  
    if WheelDelta > 0 then Value := Value + 1 + 9*Byte(GetKeyState(VK_SHIFT) < 0);
    if WheelDelta < 0 then Value := Value - 1 - 9*Byte(GetKeyState(VK_SHIFT) < 0);
  
    Focus;
  
    if Assigned(OnChange) then
      OnChange(Self);
  end;
  
  
  procedure TKMNumericEdit.ButtonClick(Sender: TObject; Shift: TShiftState);
  begin
    if Sender = fButtonDec then
      Value := Value - GetMultiplicator(Shift)
    else
    if Sender = fButtonInc then
      Value := Value + GetMultiplicator(Shift)
    else
      Exit;
  
    Focus;
  
    if Assigned(OnChange) then
      OnChange(Self);
  end;
  
  
  procedure TKMNumericEdit.SetSharedHint(aHint: UnicodeString);
  begin
    Hint := aHint;
    fButtonInc.Hint := aHint;
    fButtonDec.Hint := aHint;
  end;
  
  
  procedure TKMNumericEdit.SetTop(aValue: Integer);
  begin
    inherited;
  
    fButtonDec.Top := Top;
    fButtonInc.Top := Top;
  end;
  
  
  procedure TKMNumericEdit.SetLeft(aValue: Integer);
  begin
    inherited;
  
    fButtonDec.Left := Left;
    fButtonInc.Top := Left + Width - 20;
  end;
  
  
  procedure TKMNumericEdit.SetEnabled(aValue: Boolean);
  begin
    inherited;
    fButtonDec.Enabled := fEnabled;
    fButtonInc.Enabled := fEnabled;
  end;
  
  
  procedure TKMNumericEdit.SetVisible(aValue: Boolean);
  begin
    inherited;
    fButtonDec.Visible := fVisible;
    fButtonInc.Visible := fVisible;
  end;
  
  
  function TKMNumericEdit.GetSelfAbsLeft: Integer;
  begin
    Result := AbsLeft + 20; // Need to add extra +1 because of left button border
  end;
  
  
  function TKMNumericEdit.GetSelfWidth: Integer;
  begin
    Result := fWidth - 40; // Need to substruct extra -2 because of 2 buttons borders
  end;
  
  
  function TKMNumericEdit.IsCharValid(Key: WideChar): Boolean;
  begin
    Result := SysUtils.CharInSet(Key, ['0'..'9']);
  end;
  
  
  procedure TKMNumericEdit.SetValue(aValue: Integer);
  begin
    fValue := EnsureRange(aValue, ValueMin, ValueMax);
    fText := IntToStr(fValue);
  
    //External Value assignment should not generate OnChange event
  end;
  
  
  procedure TKMNumericEdit.ValidateText;
  var
    I: Integer;
  begin
    //Validate contents
    for I := Length(fText) downto 1 do
    if not KromUtils.CharInSet(fText[I], ['0'..'9']) then
    begin
      Delete(fText, I, 1);
      if CursorPos >= I then //Keep cursor in place
        CursorPos := CursorPos - 1;
    end;
  
    if fText = '' then
      Value := 0
    else
      Value := StrToInt(fText);
  
    if Assigned(OnChange) then
      OnChange(Self);
  end;
  
  
  procedure TKMNumericEdit.Paint;
  var
    Col: TColor4;
    RText: UnicodeString;
    OffX: Integer;
  begin
    inherited;
  
    TKMRenderUI.WriteBevel(AbsLeft + 20, AbsTop, Width - 40, Height);
    if fEnabled then Col:=$FFFFFFFF else Col:=$FF888888;
  
    RText := Copy(fText, fLeftIndex+1, Length(fText)); //Remove characters to the left of fLeftIndex
  
    PaintSelection;
  
    TKMRenderUI.WriteText(AbsLeft+24, AbsTop+3, 0, fText, fFont, taLeft, Col); //Characters that do not fit are trimmed
  
    //Render text cursor
    if (csFocus in State) and ((TimeGet div 500) mod 2 = 0) then
    begin
      SetLength(RText, CursorPos - fLeftIndex);
      OffX := AbsLeft + 22 + gRes.Fonts[fFont].GetTextSize(RText).X;
      TKMRenderUI.WriteShape(OffX, AbsTop+2, 3, Height-4, Col, $FF000000);
    end;
  end;
  
  
  