-
Content Count
112 -
Joined
-
Last visited
Community Reputation
30 ExcellentRecent Profile Visitors
-
promise Introducing My Delphi TFuture PPL For Thread Safe UI
bravesofts replied to bravesofts's topic in I made this
Thank you very much my friends.. especially my dear Dalija -
promise Introducing My Delphi TFuture PPL For Thread Safe UI
bravesofts replied to bravesofts's topic in I made this
💬 Is Future an accurate name? From a theoretical computer science and functional programming perspective, yes — the term Future (or Promise) has a well-established meaning: A Future<T> represents a value that will be available at some point in the future. But in my critique is fair in the Delphi-specific context, because Delphi’s TFuture<T> is not particularly “intelligent” in how it informs you when the value is ready. Instead, it expects you to pull (.Value) or manually monitor its .Status. Delphi’s TFuture<T> is more like a deferred computation container than a true reactive future like in JavaScript (Promise), Java (CompletableFuture) 🧠 Final Thought If Delphi’s TFuture<T> had a callback or allowed chaining with .OnComplete(...), then the name “Future” would feel more earned. As it stands: 🔹 It’s a Future — but you must chase it. 🔸 It holds a value — but it won’t tell you when. The Accurate name for it in my opinion is this "HalfPromise" Fits Perfectly. It promises a result... eventually. But it doesn't promise to tell you when it's ready. No OnComplete, no Then, no observer pattern — you're left guessing or blocking. So it's not a full Promise, just... half of one. what you think friends ? -
promise Introducing My Delphi TFuture PPL For Thread Safe UI
bravesofts replied to bravesofts's topic in I made this
but how "my some code" that is between knows exactlly how to continue while future still not ready, inorder to avoid app touching in bad time the LFuture.Value // use future value there is no callback in tfuture to use unless monitoring her status with while loop which also freeze the app -
promise Introducing My Delphi TFuture PPL For Thread Safe UI
bravesofts replied to bravesofts's topic in I made this
could you please introduce real work scenario where Future blocking call is usefull. -
promise Introducing My Delphi TFuture PPL For Thread Safe UI
bravesofts posted a topic in I made this
Delphi Async Helpers with TFuture<T>: This demo project provides a clean and safe way to perform background operations in Delphi using TTask and TFuture<T>, with a focus on keeping the UI responsive and preventing memory leaks. 📦 What's Inside API.Helpers.pas: A generic async helper unit Main.View.pas: A VCL form demonstrating good and bad usage of futures and background tasks. 🔧 Helper Static Class: TSimpleFuture<T> A simple abstraction over TTask.Future<T> and TTask.Run to make async coding easier. type TConstProc<T> = reference to procedure (const Arg1: T); TSimpleFuture<T> = class class procedure Run(const aQuery: TFunc<T>; const aReply: TConstProc<T>); static; class procedure RunFuture(const aQuery: TFunc<T>; const aReply: TConstProc<T>); static; end; 🖼 Demo Form: `Main.View.pas` The form includes buttons that demonstrate the following patterns: ✅ Correct Usage (Non-Blocking): `💪 BtnStartWithHelper`: TSimpleFuture<string>.RunFuture(function: string begin Sleep(2000); Result := TimeToStr(Now) + ' ✅ تم التنفيذ'; end, LogReply); > ✅ Background-safe > ✅ Memory-safe > ✅ Beginner-friendly ⚠️ Incorrect Usage (`BtnWrongUse`): var LFuture := TTask.Future<string>(...); LogReply(LFuture.Value); // ❌ Blocks the main thread! > ❌ This freezes the UI and defeats the purpose of async programming. ✅ Safe Manual Usage(`BtnWaitOutsideMainThread`): LFuture := TTask.Future<Integer>(...); TTask.Run( procedure begin var LValue := LFuture.Value; // Block or wait inside background thread. TThread.Queue(nil, procedure begin LogReply('Result: ' + LValue.ToString); // update UI on main thread LFuture := nil; // release `IFuture<T>` reference to avoid Memory Leak (TCompleteEventWrapper etc of internal thread pool that service the TTask). end); end); > ✅ Keeps UI free. > ✅ Releases `LFuture` to prevent leaks. 🧪 Simulating `IFuture<T>` with `ITask`(`BtnSimulateIFuture`): var LResult: string; LFuture := TTask.Run(...); TTask.Run(procedure begin LFuture.Wait; // Call wait inside background thread like IFuture<T>.Value does .. TThread.Queue(nil, procedure begin LogReply(LResult); LFuture := nil; // release `IFuture<T>` reference to avoid Memory Leak (TCompleteEventWrapper etc of internal thread pool that service the TTask). end); end); > 🧠 A useful trick for simulating `Future.Value` behavior without using `TFuture<T>`. 🔁 Future Monitoring Pattern(`BtnMonitorSolution`): A more advanced way to ensure task completion: var LFuture := TTask.Future<string>(...); TTask.Run(procedure begin while not (LFuture.Status in [TTaskStatus.Completed, TTaskStatus.Canceled, TTaskStatus.Exception]) do TThread.Sleep(100); // Reduce CPU Usage ..(Check every 100 Millisec). TThread.Queue(nil, procedure begin if LFuture.Status = TTaskStatus.Completed then LogReply(LFuture.Value) else LogReply('Future Failled or Canceled !!'); LFuture := nil; // release `IFuture<T>` reference to avoid Memory Leak (TCompleteEventWrapper etc of internal thread pool that service the TTask). end); end); 🧼 Best Practices (in my opinion now): ✅ Do : - Use `TThread.Queue` to update UI - Use `TFuture.Value` **only from background threads** - Set `LFuture := nil` to release memory ❌ Don’t : - Call `.Value` on the main thread. - Forget to release `IFuture<T>` reference to avoid Memory Leak (TCompleteEventWrapper etc of internal thread pool that service the TTask). - Update UI directly from background threads. 🧰 Requirements: - Delphi XE7+ - VCL application (not mandatory, but my Demo is VCL) - `System.Threading` unit. 📜 License Free to use and modify in any personal or commercial project. 🧠 Design Philosophy: What Future Really Means In general software design, a Future is an abstraction that represents a promise of a result to be available in the future. It is not intended to be synchronously accessed using .Value, especially not from the main thread. ❌ Misconception: Using .Value Is Asynchronous The Future is not designed for synchronous use — instead, it should be part of a fully asynchronous programming style, ideally involving a callback mechanism. Calling .Value is essentially a blocking call, which defeats the purpose of using Future in the first place. ✅ The Core Idea Behind Future The essence of the Future abstraction is: 🔹 A promise for a future result without blocking the current thread, preferably using a callback to handle the result when it’s ready. So using .Value is almost equivalent to Task.Wait — not a true asynchronous pattern ⚠️ Using .Value on the Main Thread Is Misleading! One of the most common pitfalls developers face with IFuture<T> in Delphi is the assumption that it is meant to be accessed using .Value. In reality, this goes against the very design philosophy of Future in most programming languages. In Delphi, calling .Value internally does something like this: function TFuture<T>.GetValue: T; begin Wait; // ⛔ This blocks the current thread! Result := FResult; end; So, it's not just about when the computation starts — it’s about how you consume the result in a way that doesn't harm the user experience. 🔄 Summary .Value = Blocking = like Wait Future's goal = Non-blocking promise, best used with callbacks Using .Value in UI = ❌ Breaks the async model, risks freezing UI Best practice = Use background thread + TThread.Queue for result delivery ## 🙌 Contributions Feel free to open an issue or PR if you want to extend this helper for things like: - Cancellation - Progress reporting - `Future.ContinueWith(...)` chaining. 🔧 if you find any Remarks or Suggestions , please Notify me bellow. Thank you for your Time my Friends. My Ready to use Github Repo here. -
future day TDateTime Extension for Day Name Lookup with Offset and Locale
bravesofts posted a topic in I made this
unit API.DateUtils; interface uses System.SysUtils, System.DateUtils; type TDateTimeHelper = record helper for TDateTime // Get the future day or the past day in same-function.. function DayOfWeek(const aDaysCount: Integer; aLocaleName: string = ''): string; end; implementation function TDateTimeHelper.DayOfWeek(const aDaysCount: Integer; aLocaleName: string): string; //const // cDaysSysUtils : array[1..7] of string =( // 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' // ); // cDaysDateUtils : array[1..7] of string =( // 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday' // ); var LDisplayFormat: TFormatSettings; LTargetDate: TDateTime; begin if aLocaleName <> '' then LDisplayFormat := TFormatSettings.Create(aLocaleName) else LDisplayFormat := TFormatSettings.Create; // default OS locale LTargetDate := IncDay(Self, aDaysCount); // Result := cDaysSysUtils[System.SysUtils.DayOfWeek(LTargetDate)]; // Result := cDaysDateUtils[System.DateUtils.DayOfTheWeek(LTargetDate)]; Result := FormatDateTime('dddd', LTargetDate, LDisplayFormat); end; end. then use it like this: procedure TMainView.BtnTestClick(Sender: TObject); begin ShowMessage('In 200 days, it will be: ' + Today.DayOfWeek(200, 'ar-DZ')); ShowMessage('200 days ago, it was: ' + Today.DayOfWeek(-200, 'ar-DZ')); end; Helper for TDateTime to get the name of the day (e.g., Monday, Tuesday) based on a given day offset. It supports both future and past dates by adding or subtracting a number of days. The helper function returns the localized name of the day of the week for the current date, adjusted by aDaysCount. If aLocaleName is provided, it uses that locale; otherwise, the default operating system locale is used. -
tmainmenu identify whether a TMenuItem is a top-level menu bar item
bravesofts replied to bravesofts's topic in VCL
Thanks, but I actually did try that earlier — checking if TMenuItem.Parent = nil. Unfortunately, in my specific case, it didn’t give the expected result. Even top-level menu items were affected, possibly due to the way OwnerDraw, when my MainMenu is hosted by TToolbar. I also checked the documentation, and while Parent should be nil for main menu items, it didn’t behave consistently in my setup. That’s why I started exploring alternatives like checking the Windows hSubMenu via MENUITEMINFO. Appreciate the suggestion though — it’s good in theory, just didn’t work reliably in my case.- 12 replies
-
tmainmenu identify whether a TMenuItem is a top-level menu bar item
bravesofts replied to bravesofts's topic in VCL
Thanks, that worked correctly! However, I'm still not knowing the native way to distinguish between true menu bar items (top-level items in the TMainMenu) and internal submenu items !- 12 replies
-
tmainmenu identify whether a TMenuItem is a top-level menu bar item
bravesofts replied to bravesofts's topic in VCL
procedure TdmUI.FixMenuCaptions(aMenu: TMainMenu); var I, K: Integer; procedure FixItem(aItem: TMenuItem); var J: Integer; begin if (Pos('&', aItem.Caption) = 0) and (not aItem.IsLine) then // and // (not IsMenuBarItem(aMenu, aItem)) then //or (aItem.Count > 0) or (aItem.Tag <> 2) aItem.Caption := '&' + aItem.Caption; // Add & if missing for J := 0 to aItem.Count - 1 do FixItem(aItem.Items[J]); end; begin for I := 0 to aMenu.Items.Count - 1 do for k := 0 to aMenu.items[I].Count -1 do FixItem(aMenu.Items[I]); end; I tried that approach, but it still adds the & to top-level menu bar items as well — so the issue remains. Even with separate loops or skipping directly in the main loop, the FixItem() call still affects them unless I explicitly check whether the item is a menu bar item.- 12 replies
-
tmainmenu identify whether a TMenuItem is a top-level menu bar item
bravesofts replied to bravesofts's topic in VCL
I know about using the Tag property, but I'm aiming to write a clean, universal solution or design pattern that works reliably with any dropped TMainMenu, without relying on manual tagging or special-case logic. I want something that auto-detects whether a TMenuItem is a menu bar item based purely on its structure, so it can be reused across different forms and menus with minimal setup.- 12 replies
-
tmainmenu identify whether a TMenuItem is a top-level menu bar item
bravesofts posted a topic in VCL
In Delphi VCL, I'm trying to identify whether a TMenuItem is a top-level menu bar item (a MenuBarItem) as opposed to a regular submenu item. I'm currently using this check: if aItem.Count > 0 then But this returns True for both top-level menu items (e.g., "File", "Edit" in the main menu bar) and submenu items that have their own children (like "Recent Files" under "File"). Is there a more reliable way—perhaps using TMenuItem properties or GetMenuItemInfo from the Windows API—to distinguish true menu bar items from submenu items with children? i'm using this: function IsMenuBarItem(aMenu: TMainMenu; aItem: TMenuItem): Boolean; var LInfo: MENUITEMINFOW; LHMenu: HMenu; I: Integer; begin Result := False; LHMenu := aMenu.Handle; ZeroMemory(@LInfo, SizeOf(LInfo)); LInfo.cbSize := SizeOf(LInfo); LInfo.fMask := MIIM_SUBMENU; for I := 0 to GetMenuItemCount(LHMenu) - 1 do begin if GetMenuItemInfoW(LHMenu, I, True, LInfo) then begin if LInfo.hSubMenu = aItem.Handle then begin Result := True; Exit; end; end; end; end; is possible to avoid iterating all items in the main menu if i trying to determine whether a given TMenuItem is a menu bar item, using only information from the TMenuItem itself ! --- 🧩 Context: The reason I'm doing this is to work around the automatic access key (Alt + key) assignment that Windows applies when menu items don't have an & in their caption. Even after setting: TMainMenu.AutoHotkeys := maManual; the issue persisted. I'm working in a Right-to-Left (BiDiMode) environment with OwnerDraw := True, and the system still assigns hotkeys unless the & is explicitly added to the menu captions. 👉Hint: the TMainMenu is hosted by TToolBar and not the Form ! -- 🛠️ MySolution: Add "&" to Non-MenuBar Items Here's the utility I use to ensure all non-menu bar items have an & prefix: procedure TdmUI.FixMenuCaptions(aMenu: TMainMenu); var I: Integer; procedure FixItem(aItem: TMenuItem); var J: Integer; begin if (Pos('&', aItem.Caption) = 0) and (not aItem.IsLine) and (not IsMenuBarItem(aMenu, aItem)) then //or (aItem.Count > 0) or (aItem.Tag <> 2) aItem.Caption := '&' + aItem.Caption; // Add & if missing for J := 0 to aItem.Count - 1 do FixItem(aItem.Items[J]); end; begin for I := 0 to aMenu.Items.Count - 1 do FixItem(aMenu.Items[I]); end; This approach above ensures that only submenu items get the &, preserving the expected behavior on the menu bar, especially in right-to-left and owner-drawn setups.- 12 replies
-
If full WinRT control support were available, we could simply use the built-in RangeSelector control: 🔗 RangeSelector I realize this is part of the XAML Island Surface Controls, but it's still a native Windows control — part of the OS-level UI framework.
- 7 replies
-
- delphi xe7
- trackbar
-
(and 1 more)
Tagged with:
-
I have a TMainMenu hosted on a TToolBar on my form. In my application's .dpr file (before anything else, especially before the TMainMenu is created), I call Screen.MenuFont to set a custom font. Initially, everything works perfectly — the menu displays using my custom font. However, when I change the monitor's DPI scaling while the application is running, I notice that the MainMenu font resets back to the default system font. I tried handling the OnAfterMonitorDpiChanged event by unhosting the TMainMenu from the TToolBar, resetting Screen.MenuFont to my custom settings again, and then rehosting the menu — but it doesn't seem to have any effect. How can I correctly reapply my custom MenuFont when the monitor DPI changes during runtime?
-
- tmenu
- screen font
-
(and 1 more)
Tagged with:
-
How to combine two methods with the same structure using RTTI?
bravesofts replied to bravesofts's topic in Algorithms, Data Structures and Class Design
Yes, here’s a practical use case: Example: Suppose I have a legacy application with over 100 forms, and I need to log every time a form is shown. Instead of modifying each form manually or forcing them to inherit from a new base class, I use TMethodsInjector to dynamically inject a logging procedure into the OnShow event of each form at runtime — all without changing the existing form code. This approach is especially useful in scenarios where inheritance or code edits aren't feasible — such as working with third-party components, plugin-based modules, or dynamically loaded forms. That said, this class is still in a beta/concept stage and hasn’t been fully battle-tested in real-world production scenarios. If you have a more robust or elegant way to achieve this kind of dynamic behavior injection without modifying the original source, I’d be genuinely glad to hear and learn from it! -
How to combine two methods with the same structure using RTTI?
bravesofts replied to bravesofts's topic in Algorithms, Data Structures and Class Design
i succeed to have this: TMethodsInjector = class private class var fM1, fM2: TMethod; class procedure InvokeCombined; class function CombineMethods(const M1, M2: TMethod): TMethod; static; public class procedure InjectPropertyMethod(aTarget: TObject; const aPropertyName: string; aMethodAddress: Pointer; aMethodData: TObject; aOverrideOldIfAssigned: Boolean = False); static; end; implementation { TMethodsInjector } class procedure TMethodsInjector.InvokeCombined; type TProcOfObject = procedure of object; begin if Assigned(fM1.Code) then TProcOfObject(fM1)(); if Assigned(fM2.Code) then TProcOfObject(fM2)(); end; class function TMethodsInjector.CombineMethods(const M1, M2: TMethod): TMethod; begin fM1 := M1; fM2 := M2; TMethod(Result).Code := @InvokeCombined; TMethod(Result).Data := fM1.Data; end; class procedure TMethodsInjector.InjectPropertyMethod(aTarget: TObject; const aPropertyName: string; aMethodAddress: Pointer; aMethodData: TObject; aOverrideOldIfAssigned: Boolean); var LRttiContext: TRttiContext; LRttiType: TRttiType; LRttiProperty: TRttiProperty; LOldMethod, LNewMethod, LCombined: TMethod; begin if not Assigned(aTarget) then Exit; LRttiContext := TRttiContext.Create; try LRttiType := LRttiContext.GetType(aTarget.ClassType); LRttiProperty := LRttiType.GetProperty(aPropertyName); if Assigned(LRttiProperty) and (LRttiProperty.PropertyType.TypeKind = tkMethod) then begin LOldMethod := GetMethodProp(aTarget, aPropertyName); LNewMethod.Code := aMethodAddress; LNewMethod.Data := aMethodData; LCombined := LNewMethod; // Case old is nil or (assigned and must override) if Assigned(LOldMethod.Code) and not aOverrideOldIfAssigned then LCombined := CombineMethods(LOldMethod, LNewMethod); SetMethodProp(aTarget, aPropertyName, LCombined); end else raise Exception.CreateFmt('Property %s not found or is not a method on %s', [aPropertyName, aTarget.ClassName]); finally LRttiContext.Free; end; end; end. the code works perfectly, but in case of mousedown i got AccessViolation error when i release my mouse from the dragging form, here is the code: procedure FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); private procedure InternalOnMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); procedure InjectClickToButton(AButton: TButton; aWithOverride: Boolean = False); public { Public declarations } end; implementation uses API.Methods.Injector; {$R *.dfm} procedure GlobalOnClickHandler(Sender: TObject); begin ShowMessage('Injected Click!'); end; procedure TMainView.InjectClickToButton(AButton: TButton; aWithOverride: Boolean); begin TMethodsInjector.InjectPropertyMethod( AButton, 'OnClick', @GlobalOnClickHandler, Self, // or Self if inside a class aWithOverride ); end; procedure TMainView.BtnInjectWithoutOverrideClick(Sender: TObject); begin InjectClickToButton(BtnTarget); end; procedure TMainView.BtnInjectWithOverrideClick(Sender: TObject); begin InjectClickToButton(BtnTarget, True); end; procedure TMainView.BtnTargetClick(Sender: TObject); begin ShowMessage('Hi from Default'); end; procedure TMainView.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin ReleaseCapture; end; procedure TMainView.InternalOnMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin Perform(WM_SYSCOMMAND, $F012, 0); end; procedure TMainView.BtnIjectFormDragClick(Sender: TObject); begin TMethodsInjector.InjectPropertyMethod( Self, 'OnMouseDown', @TMainView.InternalOnMouseDown, Self ); end; end.