diff --git a/components/exampleswindow/ulaz_examples.lfm b/components/exampleswindow/ulaz_examples.lfm index 9c3d84c13b..55dd91e941 100644 --- a/components/exampleswindow/ulaz_examples.lfm +++ b/components/exampleswindow/ulaz_examples.lfm @@ -1,14 +1,16 @@ object FormLazExam: TFormLazExam - Left = 88 + Left = 457 Height = 400 - Top = 155 + Top = 318 Width = 781 Caption = 'Prototype Lazarus Examples Window' ClientHeight = 400 ClientWidth = 781 OnCreate = FormCreate OnDestroy = FormDestroy + OnKeyDown = FormKeyDown OnShow = FormShow + Position = poWorkAreaCenter LCLVersion = '2.3.0.0' object Memo1: TMemo AnchorSideLeft.Control = Owner @@ -28,10 +30,11 @@ object FormLazExam: TFormLazExam Lines.Strings = ( 'Memo1' ) + OnKeyDown = FormKeyDown ParentShowHint = False ReadOnly = True ScrollBars = ssAutoVertical - TabOrder = 1 + TabOrder = 2 TabStop = False end object ListView1: TListView @@ -46,6 +49,7 @@ object FormLazExam: TFormLazExam Top = 37 Width = 771 Anchors = [akTop, akLeft, akRight, akBottom] + AutoSort = False BorderSpacing.Around = 5 Columns = < item @@ -63,6 +67,8 @@ object FormLazExam: TFormLazExam TabOrder = 0 OnClick = ListView1Click OnDblClick = ListView1DblClick + OnEnter = ListView1Enter + OnExit = ListView1Exit OnKeyDown = ListView1KeyDown OnSelectItem = ListView1SelectItem end @@ -95,7 +101,7 @@ object FormLazExam: TFormLazExam OnItemClick = CheckGroupCategoryItemClick ParentShowHint = False ShowHint = True - TabOrder = 2 + TabOrder = 3 end object Splitter2: TSplitter AnchorSideLeft.Control = Owner @@ -120,8 +126,7 @@ object FormLazExam: TFormLazExam Panels = <> end object ButtonDownload: TButton - AnchorSideLeft.Control = ButtonOpen - AnchorSideLeft.Side = asrBottom + AnchorSideLeft.Control = Owner AnchorSideRight.Side = asrBottom AnchorSideBottom.Control = ButtonOpen AnchorSideBottom.Side = asrBottom @@ -134,7 +139,7 @@ object FormLazExam: TFormLazExam BorderSpacing.Left = 5 Caption = 'Download' OnClick = ButtonDownloadClick - TabOrder = 5 + TabOrder = 6 end object ButtonClose: TButton AnchorSideLeft.Control = ButtonView @@ -150,11 +155,13 @@ object FormLazExam: TFormLazExam AutoSize = True BorderSpacing.Left = 5 Caption = 'Close' + ModalResult = 11 OnClick = ButtonCloseClick - TabOrder = 6 + TabOrder = 9 end object ButtonOpen: TButton - AnchorSideLeft.Control = Owner + AnchorSideLeft.Control = ButtonDownload + AnchorSideLeft.Side = asrBottom AnchorSideRight.Side = asrBottom AnchorSideBottom.Control = StatusBar1 Left = 5 @@ -170,7 +177,7 @@ object FormLazExam: TFormLazExam TabOrder = 7 end object ButtonView: TButton - AnchorSideLeft.Control = ButtonDownload + AnchorSideLeft.Control = ButtonOpen AnchorSideLeft.Side = asrBottom AnchorSideRight.Side = asrBottom AnchorSideBottom.Control = ButtonOpen @@ -201,11 +208,9 @@ object FormLazExam: TFormLazExam BorderSpacing.Top = 5 BorderSpacing.Right = 5 OnChange = EditSearchChange - OnExit = EditSearchExit - OnKeyUp = EditSearchKeyUp + OnKeyDown = EditSearchKeyDown ParentShowHint = False ShowHint = True - TabOrder = 9 - Text = 'EditSearch' + TabOrder = 1 end end diff --git a/components/exampleswindow/ulaz_examples.pas b/components/exampleswindow/ulaz_examples.pas index 9cf5a7e17e..e4c4e7385f 100644 --- a/components/exampleswindow/ulaz_examples.pas +++ b/components/exampleswindow/ulaz_examples.pas @@ -18,14 +18,10 @@ In either case will scan the LazConfigDir (excluding Examples ???) looking for potential 'other' example projects, recognisable by a valid json file with an extension of ex-meta. -Notes - - We have a search field across the top, its requires user to press enter, - performance notwithstanding, it could be converted to update with every key press. - - David Bannon, Feb 2022 + David Bannon, Dec 2022 } {$mode objfpc}{$H+} -{.$define EXTESTMODE} +{$define EXTESTMODE} {X$define ONLINE_EXAMPLES} @@ -43,7 +39,6 @@ uses {$endif} uexampledata, uConst; - type { TFormLazExam } @@ -66,16 +61,19 @@ type procedure CheckGroupCategoryDblClick(Sender: TObject); procedure CheckGroupCategoryItemClick(Sender: TObject; {%H-}Index: integer); procedure EditSearchChange(Sender: TObject); - procedure EditSearchExit(Sender: TObject); - procedure EditSearchKeyUp(Sender: TObject; var Key: Word; {%H-}Shift: TShiftState); + procedure EditSearchKeyDown(Sender: TObject; var Key: Word; {%H-}Shift: TShiftState); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); + procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); procedure FormShow(Sender: TObject); procedure ListView1Click(Sender: TObject); procedure ListView1DblClick(Sender: TObject); + procedure ListView1Enter(Sender: TObject); + procedure ListView1Exit(Sender: TObject); procedure ListView1KeyDown(Sender: TObject; var Key: Word; {%H-}Shift: TShiftState); procedure ListView1SelectItem(Sender: TObject; {%H-}Item: TListItem; {%H-}Selected: Boolean); private + LastListViewIndex : integer; // If 0 or greater, its an index to ListView procedure BuildSearchList(SL: TStringList; const Term: AnsiString); // Copies the passed ex dir to a dir named for the Proj. // SrcDir includes name of actual dir, DestDir does not. @@ -90,8 +88,7 @@ type // Thats triggers a Lazarus Open when this window closes. function GetProjectFile(const APath: string; WriteProjectToOpen: boolean = false): boolean; procedure KeyWordSearch; - function NewLVItem(const LView: TListView; const Proj, Path, KeyWords, - Cat: string): TListItem; + procedure NewLVItem(const Proj, Path, KeyWords, Cat: string); // Displays the current content of Examples List in the listview and // populates the Category checkboxes. procedure LoadUpListView(); @@ -117,16 +114,15 @@ implementation // ------------------------ L I S T V I E W ---------------------------------- -function TFormLazExam.NewLVItem(const LView : TListView; const Proj, Path, KeyWords, Cat : string): TListItem; +procedure TFormLazExam.NewLVItem(const Proj, Path, KeyWords, Cat : string); var TheItem : TListItem; begin - TheItem := LView.Items.Add; + TheItem := ListView1.Items.Add; TheItem.Caption := Proj; TheItem.SubItems.Add(KeyWords); TheItem.SubItems.Add(Path); TheItem.SubItems.Add(Cat); - Result := TheItem; end; procedure TFormLazExam.ListView1SelectItem(Sender: TObject; Item: TListItem; Selected: Boolean); @@ -147,15 +143,15 @@ begin end; try if Ex.GetListData(Proj, Cat, Path, KeyW, True, KeyList) then begin - NewLVItem(ListView1, Proj, Path, KeyW, Cat); + NewLVItem(Proj, Path, KeyW, Cat); inc(Cnt); end; while Ex.GetListData(Proj, Cat, Path, KeyW, False, KeyList) do begin - NewLVItem(ListView1, Proj, Path, KeyW, Cat); + NewLVItem(Proj, Path, KeyW, Cat); inc(Cnt); end; finally - if KeyList <> Nil then KeyList.Free; + KeyList.Free; Screen.Cursor := crDefault; end; ButtonOpen.Enabled := false; @@ -163,6 +159,7 @@ begin ButtonView.enabled := false; Memo1.append(format(rsFoundExampleProjects, [Cnt])); StatusBar1.SimpleText := format(rsFoundExampleProjects, [Cnt]); + LastListViewIndex := -1; // start afresh end; procedure TFormLazExam.ListView1Click(Sender: TObject); @@ -179,26 +176,50 @@ begin ButtonOpen.Enabled := GetProjectFile(Ex.ExampleWorkingDir() + ListView1.Selected.Caption); end; - procedure TFormLazExam.ListView1DblClick(Sender: TObject); +// A doubleclick will select that row, but it happens after OnEnter. begin + if ListView1.Selected = Nil then exit + else + LastListViewIndex := ListView1.ItemIndex; // So other methods can find user choice ButtonDownloadClick(self); ButtonOpenClick(self); end; +procedure TFormLazExam.ListView1Enter(Sender: TObject); +begin + ListView1.ItemIndex := LastListViewIndex; // possibly -1, half highlight item 0 +end; + +procedure TFormLazExam.ListView1Exit(Sender: TObject); +begin + LastListViewIndex := ListView1.ItemIndex; // save it before we leave, we'll be back + ListView1.ClearSelection; + ListView1.ItemIndex := -1; +end; + procedure TFormLazExam.ListView1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin - if Key = vk_return then begin + if Key = VK_RETURN then begin Key := 0; + // Its possible we tabbed into ListView without "selecting" a row. + if ListView1.ItemIndex < 0 then // I don't think this can happen anymore ? + if ListView1.Items.count > 0 then + ListView1.ItemIndex := 0 // Force select first item, its half highlite ?? + else exit; ListView1DblClick(Sender); - end; + end + else if Key = VK_ESCAPE then + ModalResult := mrClose; end; // --------------------- B U T T O N S ----------------------------------------- procedure TFormLazExam.ButtonOpenClick(Sender: TObject); begin + if LastListViewIndex < 0 then exit; + ListView1.ItemIndex:= LastListViewIndex; if GetProjectFile(Ex.ExampleWorkingDir() + ListView1.Selected.Caption, True) // Sets ProjectToOpen on success and ProjectToOpen.IsEmpty then showmessage(rsExNoProjectFile) @@ -208,7 +229,9 @@ end; procedure TFormLazExam.ButtonDownloadClick(Sender: TObject); begin - if ListView1.Selected = nil then exit; // White space below entries .... + if LastListViewIndex < 0 then exit; // Can that happen ? + ListView1.ItemIndex:= LastListViewIndex; + if GetProjectFile(Ex.ExampleWorkingDir() + ListView1.Selected.Caption) then begin if Application.MessageBox(pchar(rsRefreshExistingExample) , pchar(ListView1.Selected.Caption) @@ -238,12 +261,17 @@ begin Screen.Cursor := crDefault; end; ButtonOpen.Enabled := GetProjectFile(Ex.ExampleWorkingDir() + ListView1.Selected.Caption); - + ListView1.ItemIndex := -1; // Unselect again for the Tabbers of this world. end; procedure TFormLazExam.ButtonViewClick(Sender: TObject); begin + // When we get here, we will have left the ListView and therefore triggered its onExit + // Must restore its selected before we access it ! + if LastListViewIndex < 0 then exit; // lets not be silly + ListView1.ItemIndex:= LastListViewIndex; OpenURL(BaseURL + ListView1.Selected.SubItems[2] + '/' + ListView1.Selected.Caption); + ListView1.ItemIndex := -1; end; procedure TFormLazExam.ButtonCloseClick(Sender: TObject); @@ -375,16 +403,6 @@ begin SL.Add(AWord); end; -procedure TFormLazExam.EditSearchExit(Sender: TObject); -begin - if EditSearch.Text = '' then begin - EditSearch.Hint:= rsExSearchPrompt; - EditSearch.Text := rsExSearchPrompt; - EditSearch.SelStart := 1; - EditSearch.SelLength := length(EditSearch.Text); - end; -end; - procedure TFormLazExam.KeyWordSearch(); begin Memo1.clear; @@ -395,17 +413,19 @@ end; procedure TFormLazExam.EditSearchChange(Sender: TObject); begin - if (EditSearch.Text <> '') and (EditSearch.Text <> rsExSearchPrompt) then - KeyWordSearch(); + if visible then KeyWordSearch(); end; -procedure TFormLazExam.EditSearchKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState); +procedure TFormLazExam.EditSearchKeyDown(Sender: TObject; var Key: Word; + Shift: TShiftState); begin - // Must do this here to stop LCL from selecting the text on VK_RETURN if Key = VK_RETURN then begin - Key := 0; - KeyWordSearch(); - end; + key := 0; + if ListView1.items.Count > 0 then + ListView1.SetFocus; + end + else if Key = VK_ESCAPE then + ModalResult := mrClose; end; procedure TFormLazExam.PrimeCatFilter(); @@ -439,7 +459,9 @@ begin ListView1.Column[1].AutoSize := true; ListView1.Column[2].Visible := false; ListView1.ReadOnly := True; - EditSearch.text := rsExSearchPrompt; + LastListViewIndex := -1; // Used to record ListView1.ItemIndex before Tabbing away + + EditSearch.TextHint := rsExSearchPrompt; CheckGroupCategory.Hint := rsGroupHint; Ex := nil; // These are ObjectInspector set but I believe I cannot get OI literals set in a Package ?? @@ -459,7 +481,13 @@ end; procedure TFormLazExam.FormDestroy(Sender: TObject); begin - if Ex <> nil then Ex.Free; + Ex.Free; +end; + +procedure TFormLazExam.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); +begin + if Key = VK_ESCAPE then + ModalResult := mrClose; end; procedure TFormLazExam.FormShow(Sender: TObject); @@ -467,10 +495,11 @@ var i : integer; begin Memo1.clear; + EditSearch.text := ''; // or should we resume previous search ? Top := Screen.Height div 10; Height := Screen.Height * 7 div 10; ListView1.Height:= Screen.Height * 3 div 10; - if Ex <> Nil then Ex.Free; + Ex.Free; StatusBar1.SimpleText := rsExSearchingForExamples; Ex := TExampleData.Create(); Ex.GitDir := GitDir; @@ -490,9 +519,7 @@ begin ListView1.Clear; PrimeCatFilter(); LoadUpListView(); - if EditSearch.Text <> rsExSearchPrompt then - KeyWordSearch() - else EditSearch.SetFocus; + ListView1.SetFocus; end; { Must add a FormClose event