Wlasny komponent instalacja

Wlasny komponent instalacja
KA
  • Rejestracja:prawie 21 lat
  • Ostatnio:3 dni
  • Postów:700
0

Cześć,
zmienilem ostatnio wyglad przycisku w swojej aplikacji i stwierdzilem, ze moze warto by bylo zrobic nowy komponent aby latwiej uzywac nowej zmienionej wersji kilka razy.

Przy okazji naucze sie tworzyc komponenty :)

Problem w tym, ze kompilacja idzie bez problemu, a po przebudowaniu Lazarusa ten nie chce sie uruchomi bijac we mnie komunikatem:

LazarusError.png

Zalaczam zrodlo komponentu, chodzi tutaj po prostu o przerysowanie przycisku kiedy najedziemy kursorem i kiedy zejdziemy. Pewnie problem lezy w zrodle komponentu ale nie wiem gdzie, moge prosic o pomoc/uwagi/krytyke/komentarze? :)

// W skrocie, laduje dwa obrazki, wykrywam najechanie badz zejscie kursorem i ustawiam Mouse na true albo false. Przy Paint rysuje dany obrazek.

Kopiuj
unit ChessButton;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, Buttons, Windows;

type
  TChessButton = class(TSpeedButton)
  private
    FOnMouseEnter, FOnMouseLeave : TNotifyEvent;
    ImgEnter,ImgLeave:TImage;
    FImgEnter,FImgLeave:string;
    Mouse:boolean;
  protected
    procedure CmMouseEnter(var Msg : TMessage); message CM_MOUSEENTER;
    procedure CmMouseLeave(var Msg : TMessage); message CM_MOUSELEAVE;
  public
    constructor Create(AOwner : TComponent); override;
    destructor Destroy; override;
    procedure Paint; override;
  published
    property ImageEnter : String read FImgEnter write FImgEnter;
    property ImageLeave : String read FImgLeave write FImgLeave;
    property OnMouseEnter : TNotifyEvent read FOnMouseEnter write FOnMouseEnter;
    property OnMouseLeave : TNotifyEvent read FOnMouseLeave write FOnMouseLeave;
  end;

procedure Register;

implementation

procedure Register;
begin
  {$I chessbutton_icon.lrs}
  RegisterComponents('Additional',[TChessButton]);
end;

constructor TChessButton.Create(AOwner : TComponent);
begin
  inherited Create(AOwner);

  Mouse:=false;

  if FImgEnter<>'' then
  begin
    ImgEnter:=TImage.Create(Self);
    ImgEnter.Picture.LoadFromFile(FImgEnter);
  end;

  if FImgLeave<>'' then
  begin
    ImgLeave:=TImage.Create(Self);
    ImgLeave.Picture.LoadFromFile(FImgLeave);
  end;

end;

destructor TChessButton.Destroy;
begin
  inherited;
  ImgEnter.Free;
  ImgLeave.Free;
end;


procedure TChessButton.CMMouseEnter(var Msg : TMessage);
begin

 if Assigned(FOnMouseEnter) then OnMouseEnter(Self);

 Mouse:=true;
end;

procedure TChessButton.CmMouseLeave(var Msg : TMessage);
begin

 if Assigned(FOnMouseLeave) then OnMouseLeave(Self);

 Mouse:=false;
end;

procedure TChessButton.Paint;
var
 img:TImage;
 aRect:TRect;
begin
 inherited Paint;

  aRect.Top:=0;
  aRect.Left:=0;
  aRect.Right:=Width;
  aRect.Bottom:=Height;


        if Mouse then
        begin
          Canvas.StretchDraw(aRect, ImgEnter.Picture.Bitmap);
        end
        else
        begin
          Canvas.StretchDraw(aRect, ImgLeave.Picture.Bitmap);
        end;

     Canvas.Pen.Color:=clBlack;

     Canvas.MoveTo(0, 0);
     Canvas.LineTo(0, Height);

     Canvas.MoveTo(0, Height-1);
     Canvas.LineTo(Width, Height-1);

     Canvas.MoveTo(Width-1,height);
     Canvas.LineTo(Width-1,0);

     Canvas.MoveTo(Width,0);
     Canvas.LineTo(0,0);

     Canvas.Brush.Style := bsClear;

     SetTextAlign( Canvas.handle, TA_CENTER );
     Canvas.TextOut((Round(Width/2)), aRect.Top + 7, Caption);
     SetTextAlign( Canvas.handle, TA_LEFT );
     Font.Style := [];

end;

end.

Kto nigdy nie zrobił var dupa niech pierwszy rzuci kamień.
edytowany 1x, ostatnio: karpov
LukeJL
  • Rejestracja:około 11 lat
  • Ostatnio:minuta
  • Postów:8403
0

nie wiem o co chodzi a w Delphi pisałem dawno temu, ale jak masz stack overflow to pewnie masz wieczną pętlę gdzieś w wywołaniach funkcji (jedna funkcja wywołuje drugą, potem trzecią, trzecia znowu pierwszą i stos się odkłada aż się przepełni - to jedno z możliwych wytłumaczeń ). Może spróbuj to zdebugować step into / step over i zobaczyć co się wykonuje?


edytowany 3x, ostatnio: LukeJL
KA
  • Rejestracja:prawie 21 lat
  • Ostatnio:3 dni
  • Postów:700
0

Udalo mi sie ustalic, ze zrodlo komponentu jest straszne ;) Doszedlem do momentu, ze Lazarus sie uruchamia i moge polozyc komponent na formie ale potem dzieja sie cuda ;) Popracuje nad tym dalej i zobaczymy co wymysle:)


Kto nigdy nie zrobił var dupa niech pierwszy rzuci kamień.
flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:41 minut
  • Lokalizacja:Tuchów
  • Postów:12165
1

Robisz ten kod bardzo źle i nieczytelnie, używając złych prefiksów elementów - dla zmiennych lokalnych używasz A, czyli prefiksu dla argumentów, a dla pól nie używasz w ogóle, choć powinieneś użyć prefiksu F; Bajzel się porobił, nazwy ponakładały, wstyd... :]

Kopiuj
destructor TChessButton.Destroy;
begin
  inherited;
  ImgEnter.Free;
  ImgLeave.Free;
end;

Inherited w destruktorze wołaj na samym końcu;

Kopiuj
procedure TChessButton.Paint;
var
 img:TImage;
 aRect:TRect;
begin
 inherited Paint;

To jest kupa straszna - nie rób tak, bo prosisz się o rekurencję, a w efekcie stack overflow; Malowanie komponentu zdefiniuj w całości w swojej metodzie i nie wołaj metody z klasy bazowej; To i tak nie ma sensu - najpierw wewnętrzna metoda Paint coś namaluje, a Ty później to zamalowujesz;

Kopiuj
procedure TChessButton.CMMouseEnter(var Msg : TMessage);
begin
  if Assigned(FOnMouseEnter) then OnMouseEnter(Self);
  Mouse:=true;
end;
 
procedure TChessButton.CmMouseLeave(var Msg : TMessage);
begin
  if Assigned(FOnMouseLeave) then OnMouseLeave(Self);
  Mouse:=false;
end;

Wywołuj zdarzenia FOnMouseEnter i FOnMouseLeave, jak już coś; Zresztą, klasa TSpeedButton posiada już zdefiniowane te zdarzenia, więc robi się potworny misz-masz... Powinieneś najpierw sprawdzić, czy dana bazowa klasa posiada jakieś zdarzenie, zanim je dodasz do swojej klasy;

Przy okazji - jeżeli klasa bazowa posiada interesujące Cię zdarzenie to aby było widoczne w oknie Inspektora Obiektów w przypadku klasy końcowej (dziedziczącej), wystarczy napisać słówko Property i podać nazwę zdarzenia/właściwości, np.:

Kopiuj
TChessButton = class(TSpeedButton)
{..}
published
  // uwidocznienie właściwości zdefiniowanych w klasie bazowej
  property Align;
  property Glyph;
published
  // uwidocznienie zdarzeń zdefiniowanych w klasie bazowej
  property OnMouseEnter;
  property OnMouseLeave;
end;

A jeśli Twoje zdarzenie ma być wywoływane kiedy indziej to nazwij je inaczej, aby identyfikatory nie nakładały się; Poza tym przyjęło się, że jeśli własny komponent ma dziedziczyć z innego, to powinien dziedziczyć z klasy o prefiksie TCustom, czyli w Twoim przypadku z klasy TCustomSpeedButton;

I tak jak pokazałem wyżej - to co jest już zaimplementowane w klasie bazowej, podbijasz za pomocą podanej wyżej konstrukcji (jak nie podbijesz to nie będzie widoczne w okienku właściwości, ale z poziomu kodu nadal dostępne - usunąć całkowicie nie da się); A to czego nie ma, oprogramowujesz po swojemu, według potrzeb.


Pracuję nad własną, arcade'ową, docelowo komercyjną grą z gatunku action/adventure w stylu retro (pixel art), programując silnik i powłokę gry od zupełnych podstaw, przy użyciu Free Pascala i SDL3. Więcej informacji znajdziesz na moim mikroblogu.
edytowany 8x, ostatnio: flowCRANE
KA
  • Rejestracja:prawie 21 lat
  • Ostatnio:3 dni
  • Postów:700
0

Ok, udało mi się stworzyć działający komponent! O tyle ułatwi mi to sprawę, że nie będę musiał 'klepać formatek' pracując nad aplikacją ;)

Zdaję sobie sprawę z niedoskonałości kodu, zmienne polish-english, wykorzystanie TImage - ponoć mało oszczędne, no i wykorzystanie 2 zmienny gdzie w jednej przekazuje info o tym czy obrazy zostały załadowane (Fok) a w drugiej czy kursor aktualnie znajduje się nad buttonem czy nie (FMouse).
Co do pierwszej to nie wiem jak można to zrobić lepiej, co do drugiej to na pewno jest bardziej elegancki sposób na przekazanie argumentu do Paint.

Dla mnie satysfakcjonujący jest już fakt, że działa ;)

Mimo wszystko proszę o opinie.

Kopiuj
unit ChessButton;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, Buttons, Windows, extctrls;

type
  TChessButton = class(TSpeedButton)
  private
    FOnFMouseEnter, FOnFMouseLeave : TNotifyEvent;

    Fok:integer;
    FFMouse:boolean;

    FObrazEnter,FObrazLeave:TImage;

    FImgEnter,FImgLeave:string;

    procedure CMFMouseEnter(var Message: TMessage); message CM_FMouseENTER;
    procedure CMFMouseLeave(var Message: TMessage); message CM_FMouseLEAVE;

  protected
    procedure SetImg;
    procedure Paint; override;
  public
    constructor Create(AOwner : TComponent); override;
    destructor Destroy; override;
  published
    property ImageEnter : String read FImgEnter write FImgEnter;
    property ImageLeave : String read FImgLeave write FImgLeave;
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('Additional',[TChessButton]);
end;

constructor TChessButton.Create(AOwner : TComponent);
begin
  inherited Create(AOwner);
  Font.Color:=clWhite;
  Fok:=0;
  SetImg;
end;

destructor TChessButton.Destroy;
begin
  FObrazEnter.Free;
  FObrazLeave.Free;
  inherited;
end;

procedure TChessButton.Paint;
var
 aRect:TRect;
begin
  SetImg;
  if ok<>1 then Exit;

  aRect.Top:=0;
  aRect.Left:=0;
  aRect.Right:=Width;
  aRect.Bottom:=Height;


  if FMouse then begin Canvas.StretchDraw(aRect, FObrazEnter.Picture.Bitmap); end;
  if not FMouse then begin Canvas.StretchDraw(aRect, FObrazLeave.Picture.Bitmap); end;

     Canvas.Pen.Color:=clBlack;

     Canvas.MoveTo(0, 0);
     Canvas.LineTo(0, Height);

     Canvas.MoveTo(0, Height-1);
     Canvas.LineTo(Width, Height-1);

     Canvas.MoveTo(Width-1,height);
     Canvas.LineTo(Width-1,0);

     Canvas.MoveTo(Width,0);
     Canvas.LineTo(0,0);

     Canvas.Brush.Style := bsClear;

     SetTextAlign( Canvas.handle, TA_CENTER );
     Canvas.TextOut((Round(Width/2)), aRect.Top + 7, Caption);
     SetTextAlign( Canvas.handle, TA_LEFT );
     Font.Style := [];
end;

procedure TChessButton.SetImg;
begin
  if Fok=1 then Exit;

   if (FImgEnter<>'') and (FImgLeave<>'') then
  begin
    FObrazEnter:=TImage.Create(Self);
    FObrazEnter.Picture.LoadFromFile(FImgEnter);

    FObrazLeave:=TImage.Create(Self);
    FObrazLeave.Picture.LoadFromFile(FImgLeave);

    Fok:=1;
    FMouse:=false;
  end;

end;

procedure TChessButton.CMFMouseEnter(var Message: TMessage);
begin
  FMouse:=true;
  Paint;
end;

procedure TChessButton.CMFMouseLeave(var Message: TMessage);
begin
  FMouse:=false;
  Paint;
end;


end.

Kto nigdy nie zrobił var dupa niech pierwszy rzuci kamień.
edytowany 4x, ostatnio: karpov
Patryk27
Nie pisz if FMouse=true then, tylko samo if FMouse then, podobnie jak nie if xyz = false, tylko if not xyz.
KA
Poprawione, dzięki
KA
Tak na szybko bez kompilatora SetImg w Create nie wykona się nigdy gdyż podczas tworzenia komponentu nie ustawisz właściwości których wartość sprawdzasz. W Paint przy rysowaniu bitmapy mogłeś skorzystać z if...else... i pewnie jeszcze dużo można by znaleźć ale pora spać.
flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:41 minut
  • Lokalizacja:Tuchów
  • Postów:12165
1

Nie używaj komponentów do gromadzenia danych, jeśli nigdy ich nie wyświetlasz na formularzu; Dlatego też wywal używanie TImage i skorzystaj ze zwyklej klasy, przechowującej określony obraz; Na przykład, jeśli masz 24-bitowy obraz to użyj klasy TBitmap, a jeśli chcesz mieć przezroczystość (grafikę 32-bitową) to do dyspozycji jest klasa TPortableNetworkGraphic; Obie wymienione klasy znajdują się w module Graphics;

Druga rzecz, nigdy nie używaj metody Paint bezpośrednio, o czym wielokrotnie wspominałem wcześniej - wołaj Invalidate; W pewnych przypadkach komponent nie może być przemalowany, a próba wymuszenia odmalowania skończy się wyjątkiem (np. podczas ładowania komponentu lub jego niszczenia); Dlatego też metoda Invalidate posiada w sobie odpowiednie warunki, co możesz sam sprawdzić[#]_;

Trzecia rzecz - nie podobają mi się deklaracje właściwości obrazków; Ale jestem już tak zmęczony, że jutro coś bąknę na ten temat; Tak samo co do komunikatów CM_FMouseENTER i CM_FMouseLEAVE - co to za pieroństwo? Nie znam takich;

Z innych rzeczy to przydałoby się poznać funkcję Rect oraz instrukcję wiążącą (With); Poza tym usuwaj nieużywane moduły z listy Uses; Do tego celu przeznaczona jest opcja w edytorze - PPM ☛ Refactoring ☛ Unused units ...

.. [#] Aby szybko przemieszczać się wgłąb implementacji nadpisywanych metod, zaprzyjaźnij się ze skrótami klawiszowymi Alt+Up oraz Shift+Ctrl+Up/Down; Ich przeznaczenie masz opisane w opcjach mapowania klawiszy IDE, więc sobie poczytaj i poćwicz.


Pracuję nad własną, arcade'ową, docelowo komercyjną grą z gatunku action/adventure w stylu retro (pixel art), programując silnik i powłokę gry od zupełnych podstaw, przy użyciu Free Pascala i SDL3. Więcej informacji znajdziesz na moim mikroblogu.
edytowany 1x, ostatnio: flowCRANE
KA
  • Rejestracja:prawie 21 lat
  • Ostatnio:3 dni
  • Postów:700
0
furious programming napisał(a):

do dyspozycji jest klasa TPortableNetworkGraphic; Obie wymienione klasy znajdują się w module Graphics;

Przeglądałem źródła innych komponentów po wrzuceniu posta i faktycznie zauważyłem, że to jest używane. Poprawię to.

furious programming napisał(a):

Tak samo co do komunikatów CM_FMouseENTER i CM_FMouseLEAVE - co to za pieroństwo? Nie znam takich;

Z racji tego, że nigdy wcześniej nie pisałem takich rzeczy i nie starałem się ogarnąć świadomie takich zdarzeń po prostu to skopiowałem z jakiegoś artykułu o pisaniu komponentów. Ale z tego co widziałem w artykułach na 4p powinno się użyć CM_MOUSEENTER etc

W takim razie czas na wprowadzenie dzisiaj poprawek ;)


Kto nigdy nie zrobił var dupa niech pierwszy rzuci kamień.
flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:41 minut
  • Lokalizacja:Tuchów
  • Postów:12165
1

Przeglądałem źródła innych komponentów po wrzuceniu posta i faktycznie zauważyłem, że to jest używane. Poprawię to.

Musisz zrozumieć pewną rzecz - komponenty służą do wizualnego przedstawienia danych w oknach aplikacji; Wszystko co niewidoczne w okienkach, powinno być przechowywane w sposób minimalistyczny, w klasach o jak najmniejszej złożoności i funkcjonalności; Dlatego też np. do przechowywania łańcucha znaków używa się pola typu String, a nie komponentu TMemo, a do trzymania obrazu używa się np. instancji klasy TBitmap, TPortableNetworkGraphic, TJPEGImage itd.;

Wszystkie klasy do przechowywania obrazków posiadają wspólną funkcjonalność, zdefiniowaną w klasach o wyższym poziomie abstrakcji; Wymienione wyżej trzy klasy dziedziczą z tej samej - TFPImageBitmap - więc większa część funkcjonalności jest taka sama; One wszystkie posiadają to, co jest Ci potrzebne, czyli np. metodę LoadFromFile;

Z racji tego, że nigdy wcześniej nie pisałem takich rzeczy i nie starałem się ogarnąć świadomie takich zdarzeń po prostu to skopiowałem z jakiegoś artykułu o pisaniu komponentów. Ale z tego co widziałem w artykułach na 4p powinno się użyć CM_MOUSEENTER etc

W module Controls zdefiniowane są wszystkie stałe o prefiksie CM_ (CN_ też), w tym również CM_MOUSEENTER i CM_MOUSELEAVE; Stałe te wykorzystywane są we wszystkich klasach komponentów z biblioteki LCL, które posiadają zdarzenia OnMouseEnter i OnMouseLeave;

Dlatego też jeśli potrzebujesz w swoim komponencie dodać obsługę tych komunikatów to należy właśnie z nich skorzystać; Pozostałe stałe komunikatów zadeklarowane są w module LMessages (prefiks LM_ i SC_) oraz Windows (prefiksy WM_ i pozostałe);

I nie zapomnij o skrótach klawiszowych, które podałem w przypisie poprzedniego posta.


Pracuję nad własną, arcade'ową, docelowo komercyjną grą z gatunku action/adventure w stylu retro (pixel art), programując silnik i powłokę gry od zupełnych podstaw, przy użyciu Free Pascala i SDL3. Więcej informacji znajdziesz na moim mikroblogu.
edytowany 5x, ostatnio: flowCRANE
KA
@furious programming, te skróty są świetne. Nie wiem jak żyłem wcześniej bez nich ;)
flowCRANE
Tych skrótów jest mnóstwo, znacznie więcej, niż to co podałem; Jak raz je poznasz to pisanie kodu stanie się 4x szybsze i nie będziesz mógł bez nich żyć, więc warto znać ich jak najwięcej :)
KA
  • Rejestracja:prawie 21 lat
  • Ostatnio:3 dni
  • Postów:700
0

Ostateczna wersja komponentu. Walczyłem z tym strasznie długo ale myślę, że jest ok :) Było dużo zabawy ze sprawdzaniem czy plik z grafiką istnieje, jest ustawiona ścieżka etc więc zrobiłem property jako wskazywanie bitmapy i działa bez zarzutu.

Wrzucam poniżej ku pamięci i jednocześnie proszę o opinię.
ps. wiem, ze nazwa klasy powinna byc inna ale to na moj uzytek wiec... zostawiam.

Kopiuj
unit ChessButton;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, LResources, Forms, Controls, Windows, Graphics, Dialogs, Buttons;

type
  TChessButton = class(TSpeedButton)
  private
    { Private declarations }
    FEnter,FLeave: TBitMap;
    FMouse:boolean;

    procedure SetBitMapEnter(const AValue: TBitmap);
    procedure SetBitMapLeave(const AValue: TBitmap);
    procedure CMMouseEnter(var Message: TMessage); message CM_MOUSEENTER;
    procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
  protected
    { Protected declarations }
    procedure Paint; override;
  public
    { Public declarations }
    constructor Create(AOwner : TComponent); override;
    destructor Destroy; override;
  published
    { Published declarations }
    property ImgEnter: TBitmap read FEnter write SetBitMapEnter;
    property ImgLeave: TBitmap read FLeave write SetBitMapLeave;
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('Additional',[TChessButton]);
end;

constructor TChessButton.Create(AOwner : TComponent);
begin
  inherited Create(AOwner);
  Font.Color:=clWhite;
  FMouse:=false;
  FEnter := TBitMap.Create;
  FLeave := TBitMap.Create;
end;

destructor TChessButton.Destroy;
begin
  FreeAndNil(FEnter);
  FreeAndNil(FLeave);
  inherited;
end;

procedure TChessButton.Paint;
var
 aRect:TRect;
begin

  aRect.Top:=0;
  aRect.Left:=0;
  aRect.Right:=Width;
  aRect.Bottom:=Height;


  if FMouse then
  begin
    if FEnter<> nil then Canvas.StretchDraw(aRect, FEnter);
  end
  else
  begin
    if FLeave <> nil then Canvas.StretchDraw(aRect, FLeave);
  end;

     Canvas.Pen.Color:=clBlack;

     Canvas.MoveTo(0, 0);
     Canvas.LineTo(0, Height);

     Canvas.MoveTo(0, Height-1);
     Canvas.LineTo(Width, Height-1);

     Canvas.MoveTo(Width-1,height);
     Canvas.LineTo(Width-1,0);

     Canvas.MoveTo(Width,0);
     Canvas.LineTo(0,0);

     Canvas.Brush.Style := bsClear;

     SetTextAlign( Canvas.handle, TA_CENTER );
     Canvas.TextOut((Round(Width/2)), aRect.Top + 7, Caption);
     SetTextAlign( Canvas.handle, TA_LEFT );
     Font.Style := [];

end;

procedure TChessButton.SetBitMapEnter(const AValue: TBitmap);
begin
  FEnter.Assign(AValue);
end;

procedure TChessButton.SetBitMapLeave(const AValue: TBitmap);
begin
  FLeave.Assign(AValue);
end;

procedure TChessButton.CMMouseEnter(var Message: TMessage);
begin
    FMouse:=true;
    Invalidate;
end;

procedure TChessButton.CMMouseLeave(var Message: TMessage);
begin
    FMouse:=false;
    Invalidate;
end;

end.

Kto nigdy nie zrobił var dupa niech pierwszy rzuci kamień.
flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:41 minut
  • Lokalizacja:Tuchów
  • Postów:12165
1
Kopiuj
destructor TChessButton.Destroy;
begin
  FreeAndNil(FEnter);
  FreeAndNil(FLeave);
  inherited;
end;

Komponent i tak jest zwalniany z pamięci, więc zwykłe Free spokojnie wystarczy; Natomiast użycie FreeAndNil błędnie sugeruje, że obiekt jest celowo **Nil**owany do późniejszego użytku, co w przypadku destruktora jedynie myli, bo nic nie daje;


Kopiuj
aRect.Top:=0;
aRect.Left:=0;
aRect.Right:=Width;
aRect.Bottom:=Height;

Pisałem Ci wczesniej o funkcji Rect - użyj jej, aby skrócić kod:

Kopiuj
ARect := Rect(0, 0, Width, Height);

Na to samo wychodzi, a zajmuje jedną linijkę i jest czytelniejsze;


Kopiuj
if FMouse then
begin
  if FEnter<> nil then Canvas.StretchDraw(aRect, FEnter);
end
else
begin
  if FLeave <> nil then Canvas.StretchDraw(aRect, FLeave);
end;

pola FMouse, FEnter i FLeave nic nie mówią o swoim przeznaczeniu, a to niedobrze - powinieneś je nazwać inaczej; Przy czym **Begin**y i **End**y nie są konieczne, bo grupujesz po jednej instrukcji, a to nie jest konieczne;

Choć tej drabinki **If**ów można by się pozbyć, za pomocą dodatkowego pola - FCurrentImage; W CMMouseEnter przepisujesz referencję z FEnter do FCurrentImage, a w CMMouseLeave z FLeave (zawsze przed Invalidate); Natomiast w metodzie Paint zawsze malujesz obraz, na który wskazuje to dodatkowe pole:

Kopiuj
Canvas.StretchDraw(ARect, FCurrentImage);

Sprawdzanie czy pole wskazuje na Nil nie jest konieczne, dlatego że tym zajmuje się metoda StretchDraw w sobie; Zresztą pola FEnter i FLeave nigdy nie będą **Nil**ami, bo istnieją w pamięci przez cały czas życia komponentu;


Kopiuj
Canvas.MoveTo(0, 0);
Canvas.LineTo(0, Height);

Zły dotyk Delphi7... W LCL, klasa Canvas posiada zestaw przeładowanych, dwu- lub czteroargumentowych metod Line, od razu do podania współrzędnych dwóch punktów:

Kopiuj
Canvas.Line(0, 0, 0, Height);

Przy czym poniższy kod:

Kopiuj
Canvas.MoveTo(0, 0);
Canvas.LineTo(0, Height);

Canvas.MoveTo(0, Height-1);
Canvas.LineTo(Width, Height-1);

Canvas.MoveTo(Width-1,height);
Canvas.LineTo(Width-1,0);

Canvas.MoveTo(Width,0);
Canvas.LineTo(0,0);

o ile dobrze widzę, służy do namalowania prostokątnej ramki, dlatego też należy go zamienić na metodę FrameRect:

Kopiuj
Canvas.Brush.Color := clBlack;
FrameRect({tutaj obszar});

Przy czym pamiętaj, że FrameRect używa koloru z właściwości Brush, a nie z Pen;


Kopiuj
Round(Width/2)

Chyba nie powiesz mi, że nie znasz operatora Div? :]

Kopiuj
Width div 2

Haksiory używają do tego przesunięć bitowych, jeśli dzielnikiem jest dowolna potęga dwójki (2, 4, 8 itd.):

Kopiuj
Width shr 1

Analogicznie z mnożeniem przez potęgę dwójki, tyle że za pomocą operatora Shl;


To w sumie tyle na razie; Jak widzisz - kod króciutki, a kupa rzeczy do poprawienia; Musisz lepiej poznać RTL oraz LCL - o wiele łatwiej będzie Ci pisać kod, a ten w lepszy sposób napisany, będzie 2x krótszy i 2x szybszy od bieżącego.


Pracuję nad własną, arcade'ową, docelowo komercyjną grą z gatunku action/adventure w stylu retro (pixel art), programując silnik i powłokę gry od zupełnych podstaw, przy użyciu Free Pascala i SDL3. Więcej informacji znajdziesz na moim mikroblogu.
edytowany 4x, ostatnio: flowCRANE
KA
  • Rejestracja:prawie 21 lat
  • Ostatnio:3 dni
  • Postów:700
0

Wielkie dzięki za uwagi. Coraz bardziej zaczynam łapać o co w tym wszystkim chodzi ale jednocześnie widzę, że jeszcze mnóstwo nauki przede mną (i ciągle dochodzi więcej).

furious programming napisał(a):

Sprawdzanie czy pole wskazuje na Nil nie jest konieczne, dlatego że tym zajmuje się metoda StretchDraw w sobie; Zresztą pola FEnter i FLeave nigdy nie będą **Nil**ami, bo istnieją w pamięci przez cały czas życia komponentu;

Tutaj chciałem sprawdzić czy jakiś obraz jest załadowany żeby nie próbować malować czegoś czego nie ma. Ale faktycznie teraz widzę, że to co zrobiłem jest beznadziejne ;)

Mozna to zrobić poprzez

Kopiuj
Assigned(FEnter)

czy można w ogóle pominąć sprawdzanie tego?


Kto nigdy nie zrobił var dupa niech pierwszy rzuci kamień.
edytowany 1x, ostatnio: karpov
flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:41 minut
  • Lokalizacja:Tuchów
  • Postów:12165
1

Tutaj chciałem sprawdzić czy jakiś obraz jest załadowany żeby nie próbować malować czegoś czego nie ma.

No dobrze, ale zobacz w jaki sposób sprawdzasz ten obrazek; Ty nie patrzysz na to, czy obiekt klasy TBitmap zawiera jakiś obraz, a na to, czy obiekt klasy TBitmap w ogóle istnieje w pamięci; To dwie różne rzeczy, bo obiekt może istnieć w pamięci, ale nie zawierać w sobie obrazu (w odwrotną stronę to nie działa, chyba że instancja wyciekła do pamięci);

To sprawdzenie i tak nie ma sensu, bo Assigned(FEnter) zawsze zwróci True, dlatego że obiekt ten tworzysz w konstruktorze klasy, a zwalniasz dopiero w jej destruktorze;


Poniżej moja sugestia dotycząca dodatkowego pola z obrazkiem, a także ze zoptymalizowaną wersją metody Paint:

Kopiuj
type
  TChessButton = class(TSpeedButton)
  private
    FNormalImage: TPortableNetworkGraphic;
    FHoverImage: TPortableNetworkGraphic;
    FCurrentImage: TPortableNetworkGraphic;
  {..}
  end;

constructor TChessButton.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);

  FNormalImage := TPortableNetworkGraphic.Create();
  FHoverImage := TPortableNetworkGraphic.Create();

  FCurrentImage := FNormalImage;
end;

destructor TChessButton.Destroy();
begin
  FNormalImage.Free();
  FHoverImage.Free();

  inherited Destroy();
end;

procedure Paint();
begin
  with Canvas do
  begin
    StretchDraw(ClientRect, FCurrentImage);

    Brush.Color := clBlack;
    FrameRect(ClientRect);

    TextOut((Width - TextWidth(Caption)) div 2, 7, Caption);
  end;
end;

procedure TChessButton.CMMouseEnter(var AMessage: TLMessage);
begin
  FCurrentImage := FHoverImage;
  inherited Invalidate();
end;

procedure TChessButton.CMMouseLeave(var AMessage: TLMessage);
begin
  FCurrentImage := FNormalImage;
  inherited Invalidate();
end;

Prawda, że lepiej? :]


Pracuję nad własną, arcade'ową, docelowo komercyjną grą z gatunku action/adventure w stylu retro (pixel art), programując silnik i powłokę gry od zupełnych podstaw, przy użyciu Free Pascala i SDL3. Więcej informacji znajdziesz na moim mikroblogu.
edytowany 6x, ostatnio: flowCRANE
flowCRANE
@karpov - edytowałem post, zobacz sobie;
KA
  • Rejestracja:prawie 21 lat
  • Ostatnio:3 dni
  • Postów:700
0

Tak jak na to patrzę to napisanie takiego czegoś dla doświadczonego kodera to 5 minut włącznie z odpaleniem IDE ;)

Dzięki, bardzo doceniam Twoją pomoc!


Kto nigdy nie zrobił var dupa niech pierwszy rzuci kamień.
flowCRANE
Szczerze mówiąc mniej niż 5 minut... :P
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)