anchor.diff:  
   
   end.
  +
  
positive.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);
  var
    W: Word;
  begin
    // Text width + padding + buttons
    W := gRes.Fonts[fnt_Grey].GetTextSize(IntToStr(aValueMax)).X + 16 + 20 + 20;
  
    inherited Create(aParent, aLeft, aTop, W, 20);
  
    ValueMin := aValueMin;
    ValueMax := aValueMax;
    Value := 0;
    Focusable := True;
    fFont := fnt_Grey;
  
    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;
  end;
  
  
  function TKMNumericEdit.KeyDown(Key: Word; Shift: TShiftState): Boolean;
  begin
    Result := KeyEventHandled(Key, Shift);
    if inherited KeyDown(Key, Shift) then Exit;
  
    //Clipboard operations
    if (Shift = [ssCtrl]) and (Key <> VK_CONTROL) then
    begin
      case Key of
        Ord('C'): Clipboard.AsText := fText;
        Ord('X'): Clipboard.AsText := fText; //Does the same as Copy because we can't cut whole text and leave field empty
        Ord('V'): begin
                    Insert(Clipboard.AsText, fText, CursorPos + 1);
                    ValidateText;
                    CursorPos := CursorPos + Length(Clipboard.AsText);
                  end;
      end;
    end;
  
    case Key of
      VK_BACK:    begin Delete(fText, CursorPos, 1); CursorPos := CursorPos - 1; end;
      VK_DELETE:  Delete(fText, CursorPos + 1, 1);
      VK_LEFT:    CursorPos := CursorPos - 1;
      VK_RIGHT:   CursorPos := CursorPos + 1;
      VK_UP:      Value := Value + 1;
      VK_DOWN:    Value := Value - 1;
      VK_HOME:    CursorPos := 0;
      VK_END:     CursorPos := Length(fText);
    end;
  
    if Assigned(OnChange) then
      OnChange(Self);
  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('C'), Ord('X'), Ord('V')]);
  end;
  
  
  procedure TKMNumericEdit.KeyPress(Key: Char);
  begin
    if Length(fText) >= MaxLength then Exit;
  
    Insert(Key, fText, CursorPos + 1);
    CursorPos := CursorPos + 1; //Before ValidateText so it moves the cursor back if the new char was invalid
    ValidateText;
  end;
  
  
  function TKMNumericEdit.KeyUp(Key: Word; Shift: TShiftState): Boolean;
  begin
    Result := KeyEventHandled(Key, Shift);
    if inherited KeyUp(Key, Shift) then Exit;
  end;
  
  
  function TKMNumericEdit.MaxLength: Byte;
  begin
    if Max(Abs(ValueMax), Abs(ValueMin)) <> 0 then
      Result := Trunc(Log10(Max(Abs(ValueMax), Abs(ValueMin)))) + 1
    else
      Result := 1;
  end;
  
  
  procedure TKMNumericEdit.MouseUp(X, Y: Integer; Shift: TShiftState; Button: TMouseButton);
  begin
    inherited;
  
    CursorPos := Length(fText);
  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;
  
    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.SetCursorPos(aPos: Integer);
  var RText: UnicodeString;
  begin
    fCursorPos := EnsureRange(aPos, 0, Length(fText));
    if fCursorPos < fLeftIndex then
      fLeftIndex := fCursorPos
    else
    begin
      //Remove characters to the left of fLeftIndex
      RText := Copy(fText, fLeftIndex+1, Length(fText));
      while fCursorPos-fLeftIndex > gRes.Fonts[fFont].CharsThatFit(RText, Width-8) do
      begin
        Inc(fLeftIndex);
        //Remove characters to the left of fLeftIndex
        RText := Copy(fText, fLeftIndex+1, Length(fText));
      end;
    end;
  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 + 21; // Need to add extra +1 because of left button border
  end;
  
  
  function TKMNumericEdit.GetSelfWidth: Integer;
  begin
    Result := fWidth - 42; // Need to substruct extra -2 because of 2 buttons borders
  end;
  
  
  procedure TKMNumericEdit.SetValue(aValue: Integer);
  begin
    fValue := EnsureRange(aValue, ValueMin, ValueMax);
    fText := IntToStr(fValue);
    SetCursorPos(MaxLength);
  
    //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
  
    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;
  
  
  
negative.code: end;
  
  
  function TKMSelectableEdit.KeyUp(Key: Word; Shift: TShiftState): Boolean;
  begin
    Result := KeyEventHandled(Key, Shift);
    if inherited KeyUp(Key, Shift) or ReadOnly then Exit;
  