Gustav Schubert
Members-
Content Count
114 -
Joined
-
Last visited
-
Days Won
1
Everything posted by Gustav Schubert
-
ClientArea painting problem when MainMenu wraps/unwraps
Gustav Schubert replied to Gustav Schubert's topic in FMX
A wrapping or unwrapping MainMenu is one way to cause trouble in the process of setting ClientWidth and ClientHeight. But there is another, when the call to SetWindowPos in procedure TWinWindowHandle.SetWindowSizeByClientSize causes the height of the Window to be clipped! This happens to be the case when ClientHeight is greater than the height of the screen. So, it is possible to construct a test case where you can see the same distortion, but without using a MainMenu! Start minimal app, click on Button1, then click on Button2, Bingo. type TForm1 = class(TForm) Button1: TButton; Button2: TButton; procedure FormCreate(Sender: TObject); procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); private mch: Integer; end; var Form1: TForm1; implementation {$R *.fmx} procedure TForm1.FormCreate(Sender: TObject); var scale: single; MaxClientHeight: Integer; h: Integer; ch: Integer; begin scale := self.Handle.Scale; h := Height; ch := ClientHeight; MaxClientHeight := Screen.WorkAreaHeight - Round(scale *(h - ch)); mch := Round(MaxClientHeight / scale); { MaxClientHeight in 'Pixel-Units' } mch := Round(MaxClientHeight / scale); end; procedure TForm1.Button1Click(Sender: TObject); begin ClientWidth := Round(0.8 * mch); // must be <> mch ClientHeight := Round(1.1 * mch); // must be > mch end; procedure TForm1.Button2Click(Sender: TObject); begin ClientWidth := mch; ClientHeight := Round(2 * mch); // must be > mch end; -
ClientArea painting problem when MainMenu wraps/unwraps
Gustav Schubert replied to Gustav Schubert's topic in FMX
Final update. (Test code now improved and amplified.) - run application - toggle ClientWidth between Wide (w) and SuperNarrow (t). - watch how content gets distorted (scaled up and down) unit FrmMain; interface uses System.SysUtils, System.Types, System.UITypes, System.UIConsts, System.Classes, FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Controls.Presentation, FMX.StdCtrls, FMX.Menus, FMX.Layouts, FMX.Objects; type TFormMain = class(TForm) procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure FormKeyUp(Sender: TObject; var Key: Word; var KeyChar: Char; Shift: TShiftState); procedure FormResize(Sender: TObject); procedure FormPaint(Sender: TObject; Canvas: TCanvas; const ARect: TRectF); procedure LandscapeBtnClick(Sender: TObject); procedure PortraitBtnClick(Sender: TObject); procedure ResetBtnClick(Sender: TObject); procedure NarrowBtnClick(Sender: TObject); procedure WideBtnClick(Sender: TObject); procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); private FSuperNarrow: Integer; FNarrow: Integer; FWide: Integer; FNormalClientHeight: Integer; ML: TStringList; fa: Integer; MaxClientHeight: Integer; MaxClientHeight1: Integer; MaxClientHeight2: Integer; MenuHeight1: Integer; MenuHeight2: Integer; LastPaintRect: TRectF; procedure InitMemoText; procedure InitMenu; procedure UpdateReport; function AddMenu(M: TMainMenu; Caption: string): TMenuItem; procedure InitItem(I: TMenuItem; fa: Integer); procedure InitRectangle; procedure SuperNarrow; public MainMenu: TMainMenu; MemoText: TText; Rectangle: TRectangle; end; var FormMain: TFormMain; implementation {$R *.fmx} procedure TFormMain.FormCreate(Sender: TObject); begin ML := TStringList.Create; FSuperNarrow := 300; FNarrow := 800; FWide := 1000; FNormalClientHeight := 600; ResetBtnClick(nil); { record this while there is no MainMenu } MaxClientHeight := Screen.WorkAreaHeight - (Height - ClientHeight); InitRectangle; InitMemoText; InitMenu; { by now we 'know' the height of the main-menu-lines } MaxClientHeight1 := MaxClientHeight - MenuHeight1; MaxClientHeight2 := MaxClientHeight - MenuHeight2; end; procedure TFormMain.FormDestroy(Sender: TObject); begin ML.Free; end; procedure TFormMain.FormKeyUp(Sender: TObject; var Key: Word; var KeyChar: Char; Shift: TShiftState); begin if KeyChar = 'a' then begin ResetBtnClick(nil); PortraitBtnClick(nil); LandscapeBtnClick(nil); end else if KeyChar = 'b' then begin ResetBtnClick(nil); Button2Click(nil); Button1Click(nil); end else if KeyChar = 'c' then begin ResetBtnClick(nil); LandscapeBtnClick(nil); Button1Click(nil); LandscapeBtnClick(nil); end else if KeyChar = 'd' then begin ResetBtnClick(nil); PortraitBtnClick(nil); end else if KeyChar = 'e' then begin ResetBtnClick(nil); LandscapeBtnClick(nil); end else if KeyChar = 'f' then begin ResetBtnClick(nil); Button1Click(nil); end else if KeyChar = 'g' then begin ResetBtnClick(nil); Button2Click(nil); end else if KeyChar = 'i' then InvalidateRect(self.ClientRect) else if KeyChar = 'l' then LandscapeBtnClick(nil) else if KeyChar = 'n' then NarrowBtnClick(nil) else if KeyChar = 'p' then PortraitBtnClick(nil) else if KeyChar = 'r' then ResetBtnClick(nil) else if KeyChar = 't' then SuperNarrow else if KeyChar = 'w' then WideBtnClick(nil) else if KeyChar = '1' then Button1Click(nil) else if KeyChar = '2' then Button2Click(nil); UpdateReport; end; procedure TFormMain.FormPaint(Sender: TObject; Canvas: TCanvas; const ARect: TRectF); begin LastPaintRect := ARect; end; procedure TFormMain.FormResize(Sender: TObject); begin UpdateReport; end; procedure TFormMain.ResetBtnClick(Sender: TObject); begin ClientWidth := FNarrow; ClientHeight := FNormalClientHeight; Top := 100; end; procedure TFormMain.LandscapeBtnClick(Sender: TObject); begin ClientWidth := FWide; ClientHeight := MaxClientHeight1; Top := 0; end; procedure TFormMain.PortraitBtnClick(Sender: TObject); begin ClientWidth := FNarrow; ClientHeight := MaxClientHeight2; Top := 0; end; procedure TFormMain.NarrowBtnClick(Sender: TObject); begin ClientWidth := FNarrow; end; procedure TFormMain.WideBtnClick(Sender: TObject); begin ClientWidth := FWide; end; procedure TFormMain.Button1Click(Sender: TObject); begin ClientWidth := FNarrow; Height := Screen.WorkAreaHeight; Top := 0; end; procedure TFormMain.Button2Click(Sender: TObject); begin ClientWidth := FWide; Height := Screen.WorkAreaHeight; Top := 0; end; procedure TFormMain.UpdateReport; begin ML.Clear; ML.Add('Button KeyChars:'); ML.Add(' r = Reset'); ML.Add(' p = Portrait'); ML.Add(' l = Landscape'); ML.Add(' 1 = Button 1'); ML.Add(' 2 = Button 2'); ML.Add(''); ML.Add('KeyChars for Test Cases:'); ML.Add(' a, b, c = Bad'); ML.Add(' d, e, f, g = Good'); ML.Add(''); ML.Add('Info:'); ML.Add(Format(' Screen.Height = %d', [Screen.Height])); ML.Add(Format(' WorkAreaHeight = %d', [Screen.WorkAreaHeight])); ML.Add(Format(' MenuHeight1 = %d', [MenuHeight1])); ML.Add(Format(' MenuHeight2 = %d', [MenuHeight2])); ML.Add(Format(' W-H = (%d, %d)', [Width, Height])); ML.Add(Format(' Client-W-H = (%d, %d)', [ClientWidth, ClientHeight])); ML.Add(Format(' Rectangle-W-H = (%.0f, %.0f)', [Rectangle.Width, Rectangle.Height])); ML.Add(Format(' PaintRect-W-H = (%.0f, %.0f)', [LastPaintRect.Width, LastPaintRect.Height])); MemoText.Text := ML.Text; end; procedure TFormMain.InitMemoText; begin MemoText := TText.Create(self); MemoText.Parent := self; MemoText.Position.X := 10.0; MemoText.Position.Y := 20.0; MemoText.TextSettings.WordWrap := False; MemoText.AutoSize := True; MemoText.Font.Family := 'Consolas'; MemoText.Font.Size := 14; MemoText.TextSettings.FontColor := claBlue; MemoText.TextSettings.HorzAlign := TTextAlign.Leading; MemoText.TextSettings.VertAlign := TTextAlign.Leading; end; procedure TFormMain.InitMenu; var i: Integer; ch1, ch2: Integer; begin MainMenu := TMainMenu.Create(self); MainMenu.Parent := self; ch1 := ClientHeight; for i in [1..8] do AddMenu(MainMenu,'Menu' + IntToStr(i)); ch2 := ClientHeight; MenuHeight1 := ch1 - ch2; for i in [9..16] do AddMenu(MainMenu,'Menu' + IntToStr(i)); ch2 := ClientHeight; MenuHeight2 := ch1 - ch2; end; function TFormMain.AddMenu(M: TMainMenu; Caption: string): TMenuItem; var j: Integer; begin result := TMenuItem.Create(M); result.Text := Caption; M.AddObject(result); for j in [1..2] do begin Inc(fa); InitItem(result, fa); end; end; procedure TFormMain.InitItem(I: TMenuItem; fa: Integer); var t: TMenuItem; begin t := TMenuItem.Create(I); t.Width := 50; t.Height := 50; t.Opacity := 1.0; t.Font.Size := 24; t.Text := 'Item' + IntToStr(fa); t.Enabled := True; t.Visible := True; t.Tag := Ord(fa); I.AddObject(t); end; procedure TFormMain.InitRectangle; begin Rectangle := TRectangle.Create(self); Rectangle.Parent := self; Rectangle.Fill.Color := claNull; Rectangle.Stroke.Color := claRed; Rectangle.Stroke.Thickness := 5.0; Rectangle.Align := TAlignLayout.Client; end; procedure TFormMain.SuperNarrow; begin ClientWidth := FSuperNarrow; end; end. When you drag the Window with the mouse, the distortion disappears. -
ClientArea painting problem when MainMenu wraps/unwraps
Gustav Schubert replied to Gustav Schubert's topic in FMX
Updated Info. Changing Window Size manually (with mouse) works, programmatically changing does not, when MainMenu wraps/unwraps. I added Wide and Narrow commands, used to toggle ClientWidth only, so that the Menu will wrap/unwrap. When I toggle programmatically, - ClientHeight stays the same - Content of window is squeezed (scaled down) when in narrow situation. If I drag with mouse to make Window wider then press Narrow key (n) - Black unpainted area at bottom edge of Window appears - Content is wrongly scaled (scaled down) If I change Window size with mouse to some other values for Width and Height, then press Reset, Wide, Narrow keys in order - Height changes - ClientHeight stays unchanged - black unpainted area does NOT appear - but content is wrongly scaled. The Height of the Window may change or not, depending on history! When I resize the Window by dragging the corner, correct scaling is restored! Drawing seems to operate on false assumption of actual ClientHeight! I can see that the content is distorted by looking at the debug-output-text. I have also seen Buttons squeezed (when I still had buttons on the test form.) Workaround: I have so far worked around the problems except for one remaining: If I start up my real application with a narrower ClientWidth so that MainMenu wraps around, the content of the Window is initially scaled too big in y-direction, so that part of the Content is clipped at the bottom of the Window. I made sure the App starts wide enough to not wrap the MainMenu, but this does not count as a workaround. Note that I do set the size of a layout component manually in code (optimizing resize count) and I need the reported ClientHeight to be correct. Updated problem description: - Scaling may be wrong under certain conditions. - This is related to MainMenu wrapping and automatic resizing! - I understand that Delphi is not drawing the MainMenu. - Delphi does not know whether the Menu will wrap or not? - How do we query the current height of the MainMenu? - The point in time when Delphi queries Height/ClientHeight is too early? --- Continuation --- Program changes: - Added OnPaint handler, in which I record LastPaintRect: TRectF. - I handle KeyChar i, calling InvalidateRect(self.ClientRect); - I print LastPaintRect in the Textual Report. - I update the Report in OnResize Now focusing on this test case: 1. change window width with mouse and watch MainMenu and ClientHeight 2. stop when MainMenu does not wrap (it is wide enough) 3. press key n to set ClientWidth to narrow 4. press space bar or key i to update Report and show LastPaintRect 5. evaluate situation Observation: - Height stays the same. - ClientHeight changes when dragging the Width. - ClientHeight does NOT change when setting ClientWidth in code (using KeyChar w and n) - PaintRect is in sync with ClientWidth/ClientHeight. Expected: - Window.Height should not change - MainMenu wraps and uses more vertical space - ClientHeight should change - Content should update and adapt to new ClientHeight. Actual: - Window.Height does not change, ok - MainMenu wraps and uses more space, ok - ClientHeight property value remains the same, wrong! ( = PerceivedClientHeight) - 'PerceivedClientHeight' is too big in this case. - Content is drawn y-distorted, condensed. - Scaling factor seems to be ActualClientHeight/PerceivedClientHeight. - Black unpainted strip appears Delphi sometimes gets behind the reality in terms of what it thinks ClientHeight is right now, and then the painting gets distorted. I have a situation in my real App, where I drop Images to a DropTarget and then set ClientWidth and ClientHeight to match the Image size. Sometimes I need to drag the same Image up to three times to get good result. Again, this only happens when MainMenu.Height (not a property) changes. Eventually I will build a minimal test case for reporting. How can you determine the actual Height of the MainMenu? How can you know if MainMenu wraps? -
A minor problem: The TEdit may attempt to process clipboard content that was not put there for it to handle. Design time steps: 1. New empty FMX Application 2. Add components Button1: TButton; Edit1: TEdit; 3. Do in FormCreate ReportMemoryLeaksOnShutdown := True; Edit1.Width := 400 // optional 4. Implement ButtonClick Copy a TBitmap to the clipboard. ( see test code below that copies a screenshot of the form ) Runtime steps: Copy TBitmap to Clipboard. (click Button) Set Focus to TEdit control. (tab to Edit) Paste from Clipboard. (via ctrl V or via context menu) Assert that Edit1.Text contains '(TBitmapSurface @'. Close App. Observe that there is a Memory leak. Observations: a) (TBitmapSurface @ 0F83E7D0) appears as Edit1.Text b) Memory leak reported after Application shutdown 29 - 36 bytes: TBitmapSurface x 2 // Pasted via Ctrl V 29 - 36 bytes: TBitmapSurface x 3 // Pasted via Context Menu Implemetation details of test form: implementation uses FMX.Platform; {$R *.fmx} procedure TForm1.FormCreate(Sender: TObject); begin ReportMemoryLeaksOnShutdown := True; Edit1.Width := 400; end; procedure TForm1.Button1Click(Sender: TObject); begin CopyBitmap; end; procedure TForm1.CopyBitmap; var bmp: TBitmap; begin bmp := TBitmap.Create(Round(ClientWidth), Round(ClientHeight)); bmp.Clear(0); if bmp.Canvas.BeginScene then try PaintTo(bmp.Canvas); finally bmp.Canvas.EndScene; end; CopyBitmapToClipboard(bmp); bmp.Free; end; procedure TForm1.CopyBitmapToClipboard(ABitmap: TBitmap); var Svc: IFMXClipboardService; begin if not Assigned(ABitmap) then Exit; if TPlatformServices.Current.SupportsPlatformService(IFMXClipboardService, Svc) then Svc.SetClipboard(ABitmap); end; end.
-
FMX TEdit: how to prevent user from pasting a TBitmap ?
Gustav Schubert replied to Gustav Schubert's topic in FMX
Reported as RSP-26546. The little problem was found in Meme-Builder-App, which is toy, but can be used as a test case. -
I made Trimm420.app, and this time I am running a TestFlight for it. https://testflight.apple.com/join/NYgZL0eZ If you always wanted to explore test flight, how it looks, this is an opportunity, just saying! My goal is to get a screenshot from iPhone X Max. ( If you are running iOS 13 Beta you can provide feedback with a screenshot more easily, they say. ) Info about the app itself: https://federgraph.de/trim-420.html There is no database and no web access from the app. It can store a small text file (Trimm-File-Auto.txt) to the app specific Documents folder. The app does not have many users. Last published version (2015) was removed from the store because I did not update the app.
-
Oops, I pasted text from Visual Studio Code, and it appears with background color. This time I pasted to notepad first, and then into here. ( VS Code gives me at least some spell checking. Since my Windows System is German, I see the red squiggly underlines here in the edit box. ) Can I use markdown?
-
I needed to look up "ingress" and "boon"in the dictionary, this is the problem. Very young sailors, sailing in a dinghy (the 420), are measuring things on their boats. But this is mostly nonsense, because of friction in the system (hysteresis). They even buy little mechanical measuring devices to measure forces in the wires. The accuracy of those measurements is also questionable. The program is an abstraction, uses as simplified model. The math behind it is very basic. You would probably need to use FE software (finite element something) which I don't have (was expensive in the past) to do it properly. The old desktop app (Delphi 2, 1996) got the force from a table (diagram in TPaintbox with line or spline). You only needed to specify the params for the curve (Weg-Kraft-Kennlinie) in order to know the force, based on what was measured on the boat once. From one known force in the 'framework' all others can be computed. The iPad app does not deal with forces at all, just the kinematics of the model. When the kids use the program, they will not go faster, just have a better understanding of how things work. But the only thing they want to do is going faster! The app is a toy. And once they are on the water, the wind and the waves will deform the rig in a way I cannot compute. There is math only in the last paragraph of the page, which I need to rewrite, I know.
-
Thanks @Rollo62. You can have up to 10000 external testers. The ipa needs to be reviewed by Apple before you can have external testers. When they have approved the ipa you can click and enable the public url to the test flight, and copy the link. https://developer.apple.com/testflight/ is the best starting point for reading.
-
OSX debugger search path problem - original FMX units before patched
Gustav Schubert posted a topic in Cross-platform
In 10.3.2, (latest Rio), I cannot get the debugger to break into a patched FMX unit included in my project. The IDE will always open the original file from ...\embarcadero\studio instead of the file in my project. I already tried to mess with project options, delphi-compiler search path or debugger source path. Do I miss a trick? For a minimal test project you can use this : New empty FMX project with Button1 and Memo1 on Form1, using a local copy of FMX.Memo in subdir fmx. procedure TForm1.Button1Click(Sender: TObject); begin log.d(Memo1.Text); end; // in FMX.Memo.pas function TCustomMemo.GetText: string; begin Result := Lines.Text; // <-- break point here end; -
OSX debugger search path problem - original FMX units before patched
Gustav Schubert replied to Gustav Schubert's topic in Cross-platform
Indeed, problem solved! Just folder name of fmx will do in Project Options, Debugger, Source path. Needed for both OSX32 and OSX64. Not required for Windows. ( For Windows targets, it is enough to include the source file in the dpr. ) -
OSX debugger search path problem - original FMX units before patched
Gustav Schubert replied to Gustav Schubert's topic in Cross-platform
Yes, I have just confirmed that it works if the unit is put into the same folder. But I have it in a subfolder, and then it does not work as expected. It will go to the unit in the project folder (if present) or it will go and find the original unit. In my real project I have all my patched FMX units (many) located in subfolder ./fmx: uses System.StartUpCopy, FMX.Forms, FrmMain in 'FrmMain.pas' {Form1}, FMX.Memo in 'fmx\FMX.Memo.pas'; -
I had exactly the same problem and solved it with xcode-select. XCode was installed, but not the default. // situation before xcode-select -p /Library/Developer/CommandLineTools // action sudo xcode-select --reset // situation after xcode-select -p /Applications/Xcode.app/Contents/Developer