Конференция "Компоненты" » Кнопка на основе картинок.
 
  • Kolan © (01.04.08 09:55) [0]
    Итак, задача — написать свою кнопку-картинку.
    То есть такая кнопка похожа на TImage, но мы задаем не одну картинку, а несколько (для разных состояний кнопки).

    В другой моей ветке:
    http://pda.delphimaster.net/?n=3&id=1206102762

    DVM дал пример такой кнопки, но
    там все сделано на основе TBitmap, я хотел бы иметь возможность загружать картинки разных форматов.

    Требования к кнопке:
    1. Возможность задавать разные картинки для разных состояний:
    Обычная, Не активная, Нажатая, мышь над кнопкой
    2. Возможность использоватеь разные форматы картинок из пункта 1.
    3. Наличие Caption, которое бы работало так же как и в TButton.
    4. Наличие Font.
    5. Наличие Action. Тоже должно работать как в обычной кнопке.

    Вопросы:
    1. Кого выбрать родителем?
    2. Правильно ли я понял, что если я для картинок буду использовать TPicture, то смогу использовать разные форматы?
    3. Action, Caption и Font уже есть у TControl. Если я просто открою к ним доступ, будут лиони работать или придется еще что-то сделать?
    4. Код каких компонентов смотреть, в качестве примера? Я например не очень представляю как рисовать картинки, где искать пример? В TImage?
    5. Любые замечения.

    ЗЫ
    DimaBr — пишу свой компонент :).
  • DimaBr © (01.04.08 10:11) [1]
    1. Если кнопка будет "реальной", то есть иметь окно (её например можно найти WinAPI и нажать с другого приложения) то родитель TCustomControl, если просто рисунок - TGraphicsControl

    2. Правильно, только количество форматов не много (BMP, ICO, EMF, WMF), для изысканных нужно будет подключать библиотеки или свои свои наработки.

    3. Action - да, Сарtion & Font - используя их вы будите отрисовывать свой заголовок на кнопке.

    4. Например TSpeedButton

    5. Замечания
    Можно не писать кнопку с нуля, а взять готовую и дополнить её.
    Например, возьмите TSpeedButton, перекройте
    procedure CMMouseEnter(var Message: TMessage); message CM_MOUSEENTER;
    procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
    procedure Paint; override;

  • Kolan © (01.04.08 10:20) [2]
    > для изысканных нужно будет подключать библиотеки или свои
    > свои наработки.

    Я так понял, что, чтобы подключить доп формат (готовый) то нужно просто скачать соотв модуль (например TPNGImage с torry) и в TypeLib прописать путь. После этого он доступен для выбора в диалогах загрузки картинки, так? Я просто не оч. понимаю как этот механизм работает.

    1. Если кнопка будет «реальной», то есть иметь окно (её например можно найти WinAPI и нажать с другого приложения) то родитель TCustomControl, если просто рисунок — TGraphicsControl
    Ясно. Наверно окно как раз ненужно.


    > Можно не писать кнопку с нуля, а взять готовую и дополнить
    > её.
    > Например, возьмите TSpeedButton, перекройте

    О, наверно это то, что надо. Я наверно понижу видимость некоторых свойств. Glyph, Flat например. Это оч. плохо? Чем нибудь грозит?
  • DimaBr © (01.04.08 10:50) [3]
    Понизить видимость нельзя
  • Kolan © (01.04.08 10:54) [4]
    > Понизить видимость нельзя

    Как нельзя? перемещю соотв свойства в protected и все.
  • Kolan © (01.04.08 10:57) [5]
    А зачем нужен FDrawing в Paint у TImage?
  • Kolan © (01.04.08 11:11) [6]
    Я вот думаю, может по аналогии с кодом DVM сделать так:

    1. Добавляю поле
    FCurrentImage: TPicture;


    2. Рисование взять из TImage

    var
     Save: Boolean;
    begin
     if csDesigning in ComponentState then
       with inherited Canvas do
       begin
         Pen.Style := psDash;
         Brush.Style := bsClear;
         Rectangle(0, 0, Width, Height);
       end;
     {Save := FDrawing;
     FDrawing := True;}

     try
       with inherited Canvas do
         StretchDraw(DestRect, CurrentImage.Graphic);
     finally
       {FDrawing := Save; }
     end;
    end;



    А вот сам это CurrentImage менять при надобности. Только вот где его менять правильно?

    Или

    Надо проверять все состояния в Paint и рисовать нужную картинку?

    И еще. Как выводит Caption? Я что-то в TSpeedButton ненайду. Просто TextOut?
  • Игорь Шевченко © (01.04.08 11:30) [7]
    Кулибин. Компонентов таких написано море.
  • DimaBr © (01.04.08 11:33) [8]
    Код DVM - я бы сказал не очень.
    Рисовать в MouseUp - извращение.
    ActualBmp.Assign(Bmp); - тоже не ахти...

    Моё предложение.
    Заводим несколько свойства типа TPicture для разных состояний
    TMyButtoState = (mbsUp, mbsHot, mbsDown, mbsDisabled);

    private
     fImages: array[TMyButtoState] of TPicture;
    property ImageHot: TPicture index mbsHot read GetImage write SetImage;



    Заводим переменную - состояние кнопки
    fButtonState: TMyButtoState;
    в зависимости от которого в методе PAINT отрисовываем копку с нужного изображение
  • Игорь Шевченко © (01.04.08 11:34) [9]
    Буквально за час (правда, без Caption)

    unit HSFlatImageButton;

    interface
    uses
     Messages, Classes, Graphics, Controls;

    type
     THSButtonState = (hsbsUp, hsbsDown, hsbsHover, hsbsDisabled);
     THSFlatImageButton = class(TGraphicControl)
     private
       FImages: array[THSButtonState] of TBitmap;
       FState: THSButtonState;
       procedure SetState(const Value: THSButtonState);
       function GetImage(const Index: THSButtonState): TBitmap;
       procedure SetImage(const Index: THSButtonState; const Value: TBitmap);
       function GetDisabledImage: TBitmap;
       function GetDownImage: TBitmap;
       function GetHoverImage: TBitmap;
       function GetUpImage: TBitmap;
       procedure SetDisabledImage(const Value: TBitmap);
       procedure SetDownImage(const Value: TBitmap);
       procedure SetHoverImage(const Value: TBitmap);
       procedure SetUpImage(const Value: TBitmap);
     protected
       procedure Paint; override;
       procedure CMMouseEnter (var Message: TMessage); message CM_MOUSEENTER;
       procedure CMMouseLeave (var Message: TMessage); message CM_MOUSELEAVE;
       procedure MouseDown(Button: TMouseButton;
         Shift: TShiftState; X, Y: Integer); override;
       procedure MouseUp(Button: TMouseButton;
         Shift: TShiftState; X, Y: Integer); override;
     public
       constructor Create (AOwner: TComponent); override;
       destructor Destroy; override;
       //for test purposes only
       property State: THSButtonState read FState write SetState;
       property UpImage: TBitmap read GetUpImage write SetUpImage;
       property DownImage: TBitmap read GetDownImage write SetDownImage;
       property HoverImage: TBitmap read GetHoverImage write SetHoverImage;
       property DisabledImage: TBitmap read GetDisabledImage write SetDisabledImage;
       property OnClick;
     end;

    implementation

    { THSFlatImageButton }

    procedure THSFlatImageButton.CMMouseEnter(var Message: TMessage);
    begin
     if Enabled then
       State := hsbsHover;
    end;

    procedure THSFlatImageButton.CMMouseLeave(var Message: TMessage);
    begin
     inherited;
     if Enabled then
       State := hsbsUp;
    end;

    constructor THSFlatImageButton.Create(AOwner: TComponent);
    var
     I: THSButtonState;
    begin
     inherited;
     Height := 16;
     Width := 16;
     ControlStyle := ControlStyle + [csReplicatable];
     FState := hsbsUp;
     for I := Low(THSButtonState) to High(THSButtonState) do
     begin
       FImages[I] := TBitmap.Create;
       FImages[I].Height := 16;
       FImages[I].Width := 16;
     end;
    end;

    destructor THSFlatImageButton.Destroy;
    var
     I: THSButtonState;
    begin
     for I := Low(THSButtonState) to High(THSButtonState) do
       FImages[I].Free;
     inherited;
    end;

    function THSFlatImageButton.GetDisabledImage: TBitmap;
    begin
     Result := GetImage(hsbsDisabled);
    end;

    function THSFlatImageButton.GetDownImage: TBitmap;
    begin
     Result := GetImage(hsbsDown);
    end;

    function THSFlatImageButton.GetHoverImage: TBitmap;
    begin
     Result := GetImage(hsbsHover);
    end;

    function THSFlatImageButton.GetImage(const Index: THSButtonState): TBitmap;
    begin
     Result := FImages[Index];
    end;

    function THSFlatImageButton.GetUpImage: TBitmap;
    begin
     Result := GetImage(hsbsUp);
    end;

    procedure THSFlatImageButton.MouseDown(Button: TMouseButton; Shift: TShiftState;
     X, Y: Integer);
    begin
     inherited;
     if Enabled then
       State := hsbsDown;
    end;

    procedure THSFlatImageButton.MouseUp(Button: TMouseButton; Shift: TShiftState;
     X, Y: Integer);
    begin
     inherited;
     if Enabled then
       State := hsbsUp;
    end;

    procedure THSFlatImageButton.Paint;
    begin
     Canvas.StretchDraw(ClientRect, FImages[FState]);
    end;

    procedure THSFlatImageButton.SetDisabledImage(const Value: TBitmap);
    begin
     SetImage(hsbsDisabled, Value);
    end;

    procedure THSFlatImageButton.SetDownImage(const Value: TBitmap);
    begin
     SetImage(hsbsDown, Value);
    end;

    procedure THSFlatImageButton.SetHoverImage(const Value: TBitmap);
    begin
     SetImage(hsbsHover, Value);
    end;

    procedure THSFlatImageButton.SetImage(const Index: THSButtonState;
     const Value: TBitmap);
    begin
     FImages[Index].Assign(Value);
     if FState = Index then
       Invalidate;
    end;

    procedure THSFlatImageButton.SetState(const Value: THSButtonState);
    begin
     if FState <> Value then
     begin
       FState := Value;
       Invalidate;
     end;
    end;

    procedure THSFlatImageButton.SetUpImage(const Value: TBitmap);
    begin
     SetImage(hsbsUp, Value);
    end;

    end.

  • DimaBr © (01.04.08 11:34) [10]

    > > Понизить видимость нельзя
    > Как нельзя? перемещю соотв свойства в protected и все.

    Перемешение ничего не даст, можете проверить
  • Kolan © (01.04.08 11:41) [11]
    > Кулибин. Компонентов таких написано море.

    Покажи удовлетворяющий сабжу. Чтобы с Caption и c Action'ом был.


    > Моё предложение.

    Ок. Последую. Только зачем тогда mbsDisabled если можно проверять просто Enabled.

    mbsHot надо ставить и снимать в CMMouseEnter|Leave, так?

    А вот где можно понять что кнопка нажата?

    Так а как рисовать Caption? Нашел это место в TSpeedButton там как-то хитро закручено через
    TButtonGlyph

  • Kolan © (01.04.08 11:43) [12]
    > Перемешение ничего не даст, можете проверить

    Действительно. В результате только CodeInsite не видит свойство, а компиляция идет нормально…

    Может тогда заглушки сделать?
  • Игорь Шевченко © (01.04.08 11:44) [13]
    Kolan ©   (01.04.08 11:41) [11]

    Любой наследник TButton,TSpeedButton,TBitBtn будет иметь Action.
    Перекрытие процедуры Paint дает тебе возможность рисовать хоть черта лысого.

    Накачай компонентов с torry.net - там кнопок с картинками как звезд на небе, посмотри, как устроены, пойми почему так, а не иначе.
  • Kolan © (01.04.08 11:48) [14]
    > Буквально за час


    > Кулибин

    …Еще нет Экшена и TPicture :)

    Благодарю. Теперь понятно где какие состояния выставлять.

    ЗЫ Не увидел где устанавливается Disabled.
  • Игорь Шевченко © (01.04.08 11:55) [15]
    Kolan ©   (01.04.08 11:48) [14]

    TPicture не нужен.


    > ЗЫ Не увидел где устанавливается Disabled.


    Не стал делать, но обычно в обработчике сообщение CM_ENABLEDCHANGED
  • DimaBr © (01.04.08 12:51) [16]

    > Игорь Шевченко ©   (01.04.08 11:34) [9]

    Класс !!!
    Проиндексировав картинки можно сокатить код
  • DimaBr © (01.04.08 12:55) [17]

    if Enabled then State := XXX;


    Можно перенести в SetState тогда

    procedure THSFlatImageButton.SetState(Value: THSButtonState);
    begin
     if not Enabled then Value := hsbsDisabled;
     if fState = Value then Exit;
     fState := Value;
     Invalidate;
    end;

  • Kolan © (01.04.08 13:13) [18]
    Ок.
  • Игорь Шевченко © (01.04.08 13:15) [19]
    DimaBr ©   (01.04.08 12:51) [16]

    У меня не вышло проиндексировать :) Я запамятовал, можно ли индексировать свойства, значением которых является объект
 
Конференция "Компоненты" » Кнопка на основе картинок.
Есть новые Нет новых   [134464   +62][b:0][p:0.003]