Cześć wszystkim!
Robie sobie w Delphi aplikację i potrzebowałem fajnych powiadomień
w stylu tych z Windowsa 10/11. Skoro już to napisałem, to może się
komuś przyda.
Co potrafi:
- Wyskakujące powiadomienia w różnych kolorach (sukces, błąd, info, ostrzeżenie)
- Animacje fade-in/fade-out
- Możesz ustawić pozycję (góra/dół, lewo/środek/prawo)
- Kolejkowanie powiadomień jak ich jest więcej
- Konfigurowalne kolory, czcionki, czas wyświetlania
Jak użyć:
ShowToast('Twoja wiadomość', ttSuccess);
Kodowane pod Embarcadero® Delphi 13. Moja wersja (Embarcadero® Delphi 13 Version 37.0.57242.3601 )
Jeśli znajdziesz bugi lub będziesz miał pomysły na ulepszenia - daj znać!
Pozdro!
Pisałem pod VCL (nie testowałem na FMX).
{******************************************************************************
Toast Notification Component for Delphi VCL
Author: FPERSON
Description:
A modern, customizable toast notification system for VCL applications.
Provides non-intrusive popup notifications with support for multiple styles,
animations, and flexible positioning. Compatible with Windows applications
using the VCL framework.
Features:
- Multiple notification types (Success, Error, Warning, Info)
- Smooth fade-in/fade-out animations
- Customizable colors, fonts, and durations
- Multiple position options (Top/Bottom, Left/Center/Right)
- Queue management for multiple notifications
- Shadow effects and modern UI design
Usage:
ShowToast('Message', ttSuccess);
Requirements:
- Delphi VCL (Visual Component Library)
- Windows OS
------------------------------------------------------------
PL
Opis:
Nowoczesny, konfigurowalny system powiadomień toast dla aplikacji VCL.
Zapewnia dyskretne wyskakujące powiadomienia z obsługą wielu stylów,
animacji i elastycznego pozycjonowania. Kompatybilny z aplikacjami
Windows wykorzystującymi framework VCL.
Funkcje:
- Wiele typów powiadomień (Sukces, Błąd, Ostrzeżenie, Info)
- Płynne animacje pojawiania i zanikania
- Konfigurowalne kolory, czcionki i czas wyświetlania
- Wiele opcji pozycjonowania (Góra/Dół, Lewo/Środek/Prawo)
- Zarządzanie kolejką dla wielu powiadomień
- Efekty cienia i nowoczesny design
Użycie:
ShowToast('Wiadomość', ttSuccess);
Wymagania:
- Delphi VCL (Visual Component Library)
- System operacyjny Windows
******************************************************************************}
unit ToastNotification;
interface
uses
Winapi.Windows, Winapi.Messages, Vcl.ExtCtrls, Vcl.Controls, Vcl.Forms,
Vcl.Graphics, System.Classes, System.SysUtils, System.Generics.Collections;
type
{ TToastPosition - Określa gdzie na ekranie ma pojawić się toast }
TToastPosition = (tpBottom, tpTop, tpCenter);
{ TToastSettings - Rekord z konfiguracją wyglądu i zachowania toastu
Pozwala na pełną personalizację: kolory, rozmiar, czas trwania, prędkość animacji i pozycję }
TToastSettings = record
BackgroundColor: TColor; // Kolor tła toastu
TextColor: TColor; // Kolor tekstu
FontSize: Integer; // Rozmiar czcionki
FontStyle: TFontStyles; // Styl czcionki (pogrubienie, kursywa, etc.)
Width: Integer; // Szerokość toastu w pikselach
Height: Integer; // Wysokość toastu w pikselach
Duration: Integer; // Czas wyświetlania w milisekundach (2000 = 2 sekundy)
AnimationSpeed: Integer; // Prędkość animacji (ms między klatkami animacji)
Position: TToastPosition; // Pozycja na ekranie (dół, góra, środek)
end;
{ TToast - Klasa zarządzająca wyświetlaniem animowanych powiadomień toast
DZIAŁANIE:
1. Toast pojawia się z animacją wjazdu (20 klatek)
2. Pozostaje widoczny przez określony czas (Duration)
3. Zjeżdża z animacją wyjazdu (20 klatek)
4. Automatycznie się niszczy po zakończeniu
STACK:
- Tosty wyświetlają się jeden nad drugim bez nakładania
- Każdy nowy toast pojawia się nad poprzednim (dla tpBottom) lub pod (dla tpTop)
- Automatyczne przesuwanie gdy toast znika
UŻYCIE:
TToast.Show(Self, 'Wiadomość'); // Domyślne ustawienia
TToast.Show(Self, 'Sukces!', TToast.SuccessSettings); // Predefiniowany styl
FUNKCJE ANIMACJI:
- Wjazd: Płynne pojawienie się z pozycji startowej do docelowej
- Czekanie: Toast pozostaje nieruchomy przez czas Duration
- Zjazd: Płynne zniknięcie w kierunku zależnym od pozycji }
TToast = class
private
FPanel: TPanel; // Panel wyświetlający toast
FTimer: TTimer; // Timer sterujący animacją
FStep: Integer; // Aktualny krok animacji (0..TotalSteps)
FShouldFree: Boolean; // Flaga wskazująca czy obiekt powinien się zwolnić
FSettings: TToastSettings; // Ustawienia toastu
FTargetY: Integer; // Docelowa pozycja Y (po zakończeniu wjazdu)
FStartY: Integer; // Początkowa pozycja Y (przed animacją wjazdu)
FOwner: TComponent; // Zapamiętany owner dla stacku
class var FActiveToasts: TObjectList<TToast>; // Lista aktywnych toastów (stack)
// Metody prywatne
procedure OnTimer(Sender: TObject); // Główna pętla animacji
procedure AnimateEntrance(Parent: TWinControl); // Animacja wjazdu
procedure AnimateExit(Parent: TWinControl); // Animacja zjazdu
procedure FreeResources; // Bezpieczne zwolnienie zasobów
procedure CalculatePosition(Parent: TWinControl); // Oblicza pozycję toastu w stacku
procedure FinishToast; // Kończy animację i zwalnia obiekt
class procedure UpdateToastPositions; // Aktualizuje pozycje wszystkich toastów w stacku
public
// Konstruktory
constructor Create(AOwner: TComponent; const Msg: string); overload;
constructor Create(AOwner: TComponent; const Msg: string;
const Settings: TToastSettings); overload;
destructor Destroy; override;
// Metody statyczne do wyświetlania toastu
class procedure Show(AOwner: TComponent; const Msg: string); overload;
class procedure Show(AOwner: TComponent; const Msg: string;
const Settings: TToastSettings); overload;
// Predefiniowane ustawienia dla różnych typów komunikatów
class function DefaultSettings: TToastSettings; // Domyślne (szary)
class function SuccessSettings: TToastSettings; // Sukces (zielony)
class function ErrorSettings: TToastSettings; // Błąd (czerwony)
class function WarningSettings: TToastSettings; // Ostrzeżenie (pomarańczowy)
class function InfoSettings: TToastSettings; // Informacja (niebieski)
// Konstruktory/destruktory klasowe
class constructor Create;
class destructor Destroy;
end;
var
DefaultToastSettings: TToastSettings; // Globalne domyślne ustawienia
implementation
{ TToast }
// ============================================================================
// INICJALIZACJA KLASOWA
// ============================================================================
{ Inicjalizacja statycznych pól klasy }
class constructor TToast.Create;
begin
FActiveToasts := TObjectList<TToast>.Create(False); // False = nie zwalnia automatycznie
end;
{ Czyszczenie statycznych pól klasy }
class destructor TToast.Destroy;
begin
FActiveToasts.Free;
end;
// ============================================================================
// INICJALIZACJA DOMYŚLNYCH USTAWIEŃ
// ============================================================================
{ Inicjalizuje globalne domyślne ustawienia toastu.
Te ustawienia są używane gdy wywołujemy TToast.Show bez podania Settings. }
procedure InitializeDefaultSettings;
begin
with DefaultToastSettings do
begin
BackgroundColor := $00333333; // Ciemny szary (RGB: 51, 51, 51)
TextColor := clWhite; // Biały tekst
FontSize := 10; // Rozmiar czcionki 10
FontStyle := [fsBold]; // Pogrubiony tekst
Width := 300; // Szerokość 300px
Height := 45; // Wysokość 45px
Duration := 2000; // Czas wyświetlania: 2 sekundy
AnimationSpeed := 15; // Animacja: klatka co 15ms (~66 FPS)
Position := tpBottom; // Pozycja: na dole ekranu
end;
end;
// ============================================================================
// PREDEFINIOWANE USTAWIENIA
// ============================================================================
{ Zwraca domyślne ustawienia toastu }
class function TToast.DefaultSettings: TToastSettings;
begin
Result := DefaultToastSettings;
end;
{ Ustawienia dla komunikatów sukcesu - zielone tło }
class function TToast.SuccessSettings: TToastSettings;
begin
Result := DefaultToastSettings;
Result.BackgroundColor := $004CAF50; // Zielony Material Design
Result.TextColor := clWhite;
end;
{ Ustawienia dla komunikatów błędu - czerwone tło }
class function TToast.ErrorSettings: TToastSettings;
begin
Result := DefaultToastSettings;
Result.BackgroundColor := $00F44336; // Czerwony Material Design
Result.TextColor := clWhite;
end;
{ Ustawienia dla komunikatów ostrzeżenia - pomarańczowe tło }
class function TToast.WarningSettings: TToastSettings;
begin
Result := DefaultToastSettings;
Result.BackgroundColor := $00FF9800; // Pomarańczowy Material Design
Result.TextColor := clBlack; // Czarny tekst dla lepszej czytelności
end;
{ Ustawienia dla komunikatów informacyjnych - niebieskie tło }
class function TToast.InfoSettings: TToastSettings;
begin
Result := DefaultToastSettings;
Result.BackgroundColor := $002196F3; // Niebieski Material Design
Result.TextColor := clWhite;
end;
// ============================================================================
// PUBLICZNE METODY STATYCZNE
// ============================================================================
{ Wyświetla toast z domyślnymi ustawieniami.
@param AOwner - Komponent właściciel (okno nadrzędne)
@param Msg - Tekst do wyświetlenia }
class procedure TToast.Show(AOwner: TComponent; const Msg: string);
begin
TToast.Create(AOwner, Msg);
end;
{ Wyświetla toast z niestandardowymi ustawieniami.
@param AOwner - Komponent właściciel
@param Msg - Tekst do wyświetlenia
@param Settings - Niestandardowe ustawienia wyglądu i zachowania }
class procedure TToast.Show(AOwner: TComponent; const Msg: string;
const Settings: TToastSettings);
begin
TToast.Create(AOwner, Msg, Settings);
end;
// ============================================================================
// KONSTRUKTORY I DESTRUKTOR
// ============================================================================
{ Konstruktor z domyślnymi ustawieniami.
Przekierowuje do konstruktora z niestandardowymi ustawieniami. }
constructor TToast.Create(AOwner: TComponent; const Msg: string);
begin
Create(AOwner, Msg, DefaultToastSettings);
end;
{ Główny konstruktor tworzący i konfigurujący toast.
KROKI TWORZENIA:
1. Określa okno nadrzędne (ParentControl)
2. Tworzy panel toastu z odpowiednimi właściwościami
3. Dodaje toast do listy aktywnych (stack)
4. Oblicza pozycję startową i docelową z uwzględnieniem innych toastów
5. Uruchamia timer animacji }
constructor TToast.Create(AOwner: TComponent; const Msg: string;
const Settings: TToastSettings);
var
ParentControl: TWinControl;
begin
inherited Create;
FSettings := Settings;
FShouldFree := False;
FOwner := AOwner;
// Określamy okno nadrzędne dla toastu
if AOwner is TWinControl then
ParentControl := TWinControl(AOwner)
else
ParentControl := Application.MainForm;
// Tworzymy panel toastu
FPanel := TPanel.Create(nil);
try
FPanel.Parent := ParentControl;
// Konfiguracja wyglądu panelu
FPanel.Caption := Msg;
FPanel.Color := FSettings.BackgroundColor;
FPanel.Font.Color := FSettings.TextColor;
FPanel.Font.Size := FSettings.FontSize;
FPanel.Font.Style := FSettings.FontStyle;
FPanel.BevelOuter := bvNone; // Bez obramowania
FPanel.Width := FSettings.Width;
FPanel.Height := FSettings.Height;
FPanel.DoubleBuffered := True; // Zapobiega migotaniu
// Optymalizacje wydajnościowe
FPanel.ParentBackground := False;
FPanel.ParentDoubleBuffered := False;
// WAŻNE: Dodaj toast do listy PRZED obliczeniem pozycji
FActiveToasts.Add(Self);
// Oblicz i ustaw pozycję (z uwzględnieniem stacku)
CalculatePosition(ParentControl);
// Pokazujemy i ustawiamy na wierzchu
FPanel.Visible := True;
FPanel.BringToFront;
// Inicjalizacja animacji
FStep := 0;
FTimer := TTimer.Create(nil);
FTimer.Interval := FSettings.AnimationSpeed;
FTimer.OnTimer := OnTimer;
FTimer.Enabled := True;
except
// W razie błędu bezpiecznie zwalniamy zasoby
FActiveToasts.Remove(Self);
FreeResources;
raise;
end;
end;
{ Destruktor - bezpiecznie zwalnia wszystkie zasoby }
destructor TToast.Destroy;
begin
FreeResources;
inherited;
end;
// ============================================================================
// METODY PRYWATNE - ZARZĄDZANIE STACKIEM
// ============================================================================
{ Aktualizuje pozycje wszystkich aktywnych toastów w stacku.
Wywoływane gdy toast znika - pozostałe tosty płynnie zajmują jego miejsce }
class procedure TToast.UpdateToastPositions;
var
I, Offset: Integer;
Toast: TToast;
ParentControl: TWinControl;
begin
if FActiveToasts.Count = 0 then
Exit;
for I := 0 to FActiveToasts.Count - 1 do
begin
Toast := FActiveToasts[I];
if not Assigned(Toast.FPanel) or not Assigned(Toast.FPanel.Parent) then
Continue;
ParentControl := TWinControl(Toast.FPanel.Parent);
// Oblicz offset na podstawie wcześniejszych toastów
Offset := 0;
case Toast.FSettings.Position of
tpBottom:
begin
// Dla każdego wcześniejszego toastu dodaj jego wysokość + odstęp
var J: Integer;
for J := 0 to I - 1 do
begin
if Assigned(FActiveToasts[J].FPanel) then
Inc(Offset, FActiveToasts[J].FPanel.Height + 10);
end;
// Aktualizuj docelową pozycję (ale tylko jeśli toast już wjechał)
if Toast.FStep > 20 then
begin
Toast.FTargetY := ParentControl.ClientHeight - Toast.FPanel.Height - 20 - Offset;
Toast.FPanel.Top := Toast.FTargetY;
end;
end;
tpTop:
begin
// Dla tpTop tosty układają się od góry w dół
var J: Integer;
for J := 0 to I - 1 do
begin
if Assigned(FActiveToasts[J].FPanel) then
Inc(Offset, FActiveToasts[J].FPanel.Height + 10);
end;
if Toast.FStep > 20 then
begin
Toast.FTargetY := 20 + Offset;
Toast.FPanel.Top := Toast.FTargetY;
end;
end;
tpCenter:
begin
// Dla center każdy toast ma swoją pozycję z offsetem
var J: Integer;
for J := 0 to I - 1 do
begin
if Assigned(FActiveToasts[J].FPanel) then
Inc(Offset, FActiveToasts[J].FPanel.Height + 10);
end;
if Toast.FStep > 20 then
begin
Toast.FTargetY := (ParentControl.ClientHeight - Toast.FPanel.Height) div 2 - Offset;
Toast.FPanel.Top := Toast.FTargetY;
end;
end;
end;
end;
end;
// ============================================================================
// METODY PRYWATNE - LOGIKA ANIMACJI
// ============================================================================
{ Oblicza pozycję startową i docelową toastu w zależności od ustawień i stacku.
Uwzględnia pozycję innych aktywnych toastów aby uniknąć nakładania }
procedure TToast.CalculatePosition(Parent: TWinControl);
var
I, Offset: Integer;
begin
// Pozycja pozioma - zawsze wyśrodkowana
FPanel.Left := (Parent.ClientWidth - FPanel.Width) div 2;
// Oblicz offset na podstawie innych aktywnych toastów
Offset := 0;
for I := 0 to FActiveToasts.Count - 1 do
begin
if FActiveToasts[I] <> Self then
begin
// Sprawdź czy toast ma ten sam FOwner i Position
if (FActiveToasts[I].FOwner = FOwner) and
(FActiveToasts[I].FSettings.Position = FSettings.Position) and
Assigned(FActiveToasts[I].FPanel) then
begin
Inc(Offset, FActiveToasts[I].FPanel.Height + 10); // 10px odstępu między toastami
end;
end;
end;
// Pozycja pionowa zależna od ustawień i offsetu od innych toastów
case FSettings.Position of
tpBottom:
begin
FStartY := Parent.ClientHeight; // Start: poniżej ekranu
FTargetY := Parent.ClientHeight - FPanel.Height - 20 - Offset; // Cel: 20px od dołu + offset
end;
tpTop:
begin
FStartY := -FPanel.Height; // Start: powyżej ekranu
FTargetY := 20 + Offset; // Cel: 20px od góry + offset
end;
tpCenter:
begin
FStartY := Parent.ClientHeight; // Start: poniżej ekranu
FTargetY := (Parent.ClientHeight - FPanel.Height) div 2 - Offset; // Cel: środek + offset
end;
end;
// Ustaw początkową pozycję
FPanel.Top := FStartY;
end;
{ Główna pętla animacji - wywoływana przez timer co AnimationSpeed ms.
Fazy animacji:
1. FStep 0-20: Animacja wjazdu (AnimateEntrance)
2. FStep 21-(TotalSteps-20): Czekanie (toast nieruchomy)
3. FStep (TotalSteps-19)-TotalSteps: Animacja zjazdu (AnimateExit) }
procedure TToast.OnTimer(Sender: TObject);
var
P: TWinControl;
TotalSteps: Integer;
begin
if FShouldFree then
Exit;
// Bezpieczeństwo - sprawdzamy czy komponenty jeszcze istnieją
if not Assigned(FPanel) or not Assigned(FPanel.Parent) then
begin
if Assigned(FTimer) then
FTimer.Enabled := False;
FreeResources;
Exit;
end;
P := TWinControl(FPanel.Parent);
Inc(FStep);
// Oblicz całkowitą liczbę kroków animacji
TotalSteps := (FSettings.Duration div FSettings.AnimationSpeed) + 40;
// FAZA 1: ANIMACJA WJAZDU (pierwsze 20 kroków)
if FStep <= 20 then
begin
AnimateEntrance(P);
end
// FAZA 2: CZEKANIE (toast widoczny bez ruchu)
else if FStep <= TotalSteps - 20 then
begin
// Toast pozostaje w miejscu - nic nie robimy
end
// FAZA 3: ANIMACJA ZJAZDU (ostatnie 20 kroków)
else
begin
AnimateExit(P);
// Sprawdzamy czy toast zjechał poza ekran
case FSettings.Position of
tpBottom:
if FPanel.Top >= P.ClientHeight then FinishToast;
tpTop:
if FPanel.Top <= -FPanel.Height then FinishToast;
tpCenter:
if FPanel.Top >= P.ClientHeight then FinishToast;
end;
end;
end;
{ Animacja wjazdu toastu na ekran.
Używa interpolacji liniowej do płynnego przejścia z FStartY do FTargetY. }
procedure TToast.AnimateEntrance(Parent: TWinControl);
var
Progress: Double;
begin
// Progress rośnie od 0 do 1 w ciągu 20 kroków
Progress := FStep / 20;
// Interpolacja liniowa: pozycja = start + (cel - start) * progress
FPanel.Top := Round(FStartY + (FTargetY - FStartY) * Progress);
end;
{ Animacja zjazdu toastu z ekranu.
Określa kierunek zjazdu w zależności od pozycji toastu. }
procedure TToast.AnimateExit(Parent: TWinControl);
var
ExitStep: Integer;
Progress: Double;
ExitStartY, ExitTargetY: Integer;
begin
// Numer kroku w fazie zjazdu (0-20)
ExitStep := FStep - (FSettings.Duration div FSettings.AnimationSpeed + 20);
Progress := ExitStep / 20;
// Określamy kierunek zjazdu
case FSettings.Position of
tpBottom:
begin
ExitStartY := FTargetY; // Start zjazdu: pozycja docelowa
ExitTargetY := Parent.ClientHeight; // Koniec zjazdu: poniżej ekranu
end;
tpTop:
begin
ExitStartY := FTargetY; // Start zjazdu: pozycja docelowa
ExitTargetY := -FPanel.Height; // Koniec zjazdu: powyżej ekranu
end;
tpCenter:
begin
ExitStartY := FTargetY; // Start zjazdu: pozycja docelowa
ExitTargetY := Parent.ClientHeight; // Koniec zjazdu: poniżej ekranu
end;
else
// Domyślnie: zjazd w dół
ExitStartY := FTargetY;
ExitTargetY := Parent.ClientHeight;
end;
// Interpolacja liniowa dla zjazdu
FPanel.Top := Round(ExitStartY + (ExitTargetY - ExitStartY) * Progress);
end;
{ Kończy animację i inicjuje proces zwalniania obiektu.
Usuwa toast ze stacku i aktualizuje pozycje pozostałych toastów }
procedure TToast.FinishToast;
begin
if Assigned(FTimer) then
FTimer.Enabled := False;
FShouldFree := True;
// Usuń ze stacku
FActiveToasts.Remove(Self);
// Aktualizuj pozycje pozostałych toastów
UpdateToastPositions;
// Bezpieczne zwolnienie przez kolejkę wiadomości
PostMessage(Application.MainForm.Handle, WM_NULL, 0, 0);
FreeResources;
end;
{ Bezpiecznie zwalnia wszystkie zasoby (timer i panel). }
procedure TToast.FreeResources;
begin
// Zatrzymaj i zwolnij timer
if Assigned(FTimer) then
begin
FTimer.Enabled := False;
FreeAndNil(FTimer);
end;
// Ukryj i zwolnij panel
if Assigned(FPanel) then
begin
FPanel.Visible := False;
FreeAndNil(FPanel);
end;
end;
// ============================================================================
// INICJALIZACJA GLOBALNA
// ============================================================================
initialization
InitializeDefaultSettings;
end.
