Search code examples
delphic++buildervcljvcl

Numeric edit control with flat button inside and no calculator


I'm writing a C++ project with RAD Studio, but this also applies to Delphi.

I need an edit control where user can only enter floats (2 decimal places) and can restore the original value (taken from a variable, not important here) clilcking on a button (actullay an icon) inside the edit control itself.

This is what I've done, using a TJvCalcEdit from JEDI library.

enter image description here

Control definition:

object Sconto1: TJvCalcEdit
  [non-important attributes...]
  ButtonFlat = True
  Glyph.Data = {
    D6020000424DD6020000000000003600000028000000100000000E0000000100
    180000000000A0020000130B0000130B00000000000000000000FFFFFFFFFFFF
    FFFFFFFFFFFFFFFFFF999EC29396C3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
    FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9198C48694CBA7BAFE8493CA72
    75B9FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8B96C5
    8695CBA7BAFEA7BAFEA7BAFEA7BAFE747EB66D71B5FFFFFFFFFFFFFFFFFFFFFF
    FFFFFFFFFFFFFFFFFFFF8493CAA7BAFEA7BAFEA7BAFEA7BAFEA7BAFEA7BAFE84
    93CA7E83CE6D71B4FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8F94C3A7BAFE
    A7BAFEA7BAFEA7BAFEA7BAFEA7BAFE8492CA8288D27B7FCA6D71B4FFFFFFFFFF
    FFFFFFFFFFFFFFFFFFFFFFFFFF8492CAA7BAFEA7BAFEA7BAFE828ECA7B82C993
    96FA6D6FB67B7FCA7B7FCA6D6FB4FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9299C2
    A5B7FE7E88CA787DC99396FA9396FA9396FA9396FA6D6FB67B7FCA7B7FCA6D6F
    B4FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7678C89396FA9396FA9396FA9396FA93
    96FA9396FA9396FA6D6FB67B7FCA7B7FCA6C6FB3FFFFFFFFFFFFFFFFFFFFFFFF
    FFFFFF7678C89396FA9396FA9396FA9396FA9396FA9396FA9396FA6D6FB67B7F
    CA7B7FCA7576B0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7678C89396FA9396FA93
    96FA9396FA9396FA9396FA9396FA6D6FB67B7FCA6266A2D6D0E2FFFFFFFFFFFF
    FFFFFFFFFFFFFFFFFF7678C89396FA9396FA9396FA9396FA9396FA9396FA9396
    FA6D6FB67B7FCA7C7EB0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7678C893
    96FA9396FA9396FA9396FA9396FA9396FA9396FA7679C66B6DACFFFFFFFFFFFF
    FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7678C89396FA9396FA9093F58B8EEC7678
    C87C7FC6ACABE5FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
    FFFF7678C88283C5A4A4E7C4C1EBFFFFFFFFFFFFFFFFFFFFFFFF}
  ImageKind = ikCustom
  DecimalPlacesAlwaysShown = False
  OnButtonClick = EScontoButtonClick
end

Method called on button click:

void __fastcall TFRigOrd::EScontoButtonClick(TObject *Sender)
{
    TJvCalcEdit* edit = dynamic_cast<TJvCalcEdit*>(Sender);
    edit->Value = oldSconto1;
}

The problem: at the end of this method a calculator popup appears below the control, requiring an action by the user. I don't want this to happen because I'm changing the value programmatically. I guess it's a default value due to the fact that such button is made for triggering the calculator. Moreover the value you see (255) appears without decimal point, with will be shown only once the calculator is closed.

enter image description here

So, can I disable this behaviour? Or can someone suggest me a solution with another control (standard, open source or free anyway)?


Solution

  • I'd use a TButtonedEdit to get the button, and to enforce floating-point input with a maximum of two decimals after the point, I'd do

    TButtonedEdit = class(ExtCtrls.TButtonedEdit)
    protected
      procedure KeyPress(var Key: Char); override;
      procedure WMPaste(var Message: TWMPaste); message WM_PASTE;
    end;
    
    ...
    
    procedure TButtonedEdit.KeyPress(var Key: Char);
      function InvalidInput: boolean;
      var
       dc: integer;
      begin
        result := false;
        if Character.IsControl(Key) then Exit;
        dc := Pos(DecimalSeparator, Text);
        if not (Key in ['0'..'9', DecimalSeparator]) then Exit(true);
        if Pos(DecimalSeparator, Text) > 0 then
        begin
          if Key = DecimalSeparator then Exit(true);
          if (Length(Text) - dc > 1)
            and (Pos(DecimalSeparator, Text) < SelStart + 1) and
            (SelLength = 0) then Exit(true);
        end;
      end;
    
    begin
      inherited;
      if InvalidInput then
      begin
        Key := #0;
        beep;
      end;
    end;
    
    procedure TButtonedEdit.WMPaste(var Message: TWMPaste);
    var
      s: string;
      i: integer;
      hasdc: boolean;
      NewText: string;
      NewSelStart: integer;
    begin
      if Clipboard.HasFormat(CF_TEXT) then
      begin
        s := Clipboard.AsText;
    
        NewText := Text;
        Delete(NewText, SelStart + 1, SelLength);
        Insert(s, NewText, SelStart + 1);
    
    
        // Validate
        hasdc := false;
        for i := 1 to Length(NewText) do
        begin
          if NewText[i] = DecimalSeparator then
            if hasdc then
            begin
              beep;
              Exit;
            end
            else
              hasdc := true
          else if not (NewText[i] in ['0'..'9']) then
          begin
            beep;
            Exit;
          end;
        end;
    
        // Trim
        if hasdc then
          NewText := Copy(NewText, 1, Pos(DecimalSeparator, NewText) + 2);
    
        NewSelStart := SelStart + Length(s);
        Text := NewText;
        SelStart := NewSelStart;
        SelLength := 0;
      end
      else
        inherited;
    end;
    

    Screenshot

    Sample demo EXE