How do you create a scrollable TW3ListMenu at run-time? - smart-mobile-studio

How do you create a scrollable TW3ListMenu at run-time?
I need something a little better for scrolling vertically through 50 items
I'd like to use a TW3ListMenu and just display the caption and a icon to the right ">" to click - then display the additional information on a secondary form.
How do I create a scrollable list of items using a TW3ListMenu?
I created the TW3ListMenu on a TW3ScrollBox, but it does not seem to scroll
Maybe its the layout ?
procedure TfrmMain.InitializeObject;
var
i: Integer;
procedure AddMenuItem(caption: string);
var
li: TW3ListItem;
begin
li := fListMenu.Items.Add;
li.Text := caption;
li.OnClick :=
procedure (Sender: TObject)
begin
//ShowMessage('You clicked: ' + (Sender as TW3ListItem).Text);
end;
end; //addmenu
begin
inherited;
{$I 'frmMain:impl'}
fHeader:= TW3HeaderControl.Create(self);
fHeader.Height:= 50;
fHeader.Title.Caption := 'Mountains';
fHeader.Title.AlignText:= taCenter;
fHeader.BackButton.Visible:= False;
fHeader.StyleClass:= 'TW3Header';
fBackButton:= TMenuButton.Create(self);
fBackButton.Caption:= 'Back';
fBackButton.Height:= 50;
fBackButton.StyleClass:= 'TMenuButton';
fBackButton.OnClick:= BackButtonClick;
fScrollBox:= TW3Scrollbox.Create(self);
fListMenu:= TW3ListMenu.Create(fScrollbox);
For i:= 1 to 46 do
AddMenuItem(IntToStr(i));
FLayout :=
Layout{1}.Client(Layout{2}.Margins(5).Spacing(5), [
Layout{3}.Top(fHeader),
Layout{4}.Client(fScrollbox),
Layout{5}.Bottom(fBackButton)]);
end;
I even tried two layouts....
fScrollBox:= TW3Scrollbox.Create(self);
fListMenu:= TW3ListMenu.Create(fScrollbox);
For i:= 1 to 46 do
AddMenuItem(IntToStr(i));
FScrollLayout :=
Layout{1}.Client(Layout{2}.Margins(5).Spacing(5), [
Layout{3}.Client(fScrollbox)]);
FLayout :=
Layout{1}.Client(Layout{2}.Margins(5).Spacing(5), [
Layout{3}.Top(fHeader),
Layout{4}.Client(fScrollLayout),
Layout{5}.Bottom(fBackButton)]);

warleyalex posted a solution on the Smart Mobile Studio form
A solution which uses the qtxlibrary

Related

Does HelpNDoc Pascal Script support structures?

I am trying to create a structure:
MyTopic
TopicID : String;
HelpID : Integer;
I wanted to create an array of these structures so I could sort them.
I have tried using this type / record syntax but it is failing.
Update
I defined this type and procedure:
type
TMyTopicRecord = record
idTopic : String;
idContextHelp : integer;
End;
procedure GetSortedTopicIDs(aTopics : array of String; size : Integer);
var
aMyTopicRecords : array of TMyTopicRecord;
temp : TMyTopicRecord;
iTopic, i, j : Integer;
begin
// Init the array
SetLength(aMyTopicRecords, size);
// Fill the array with the existing topid ids.
// Get the context ids at the same time.
for iTopic := 0 to size - 1 do
aMyTopicRecords[iTopic].idTopic := aTopics[iTopic];
aMyTopicRecords[iTopic].idContextHelp := HndTopics.GetTopicHelpContext(aTopics[iTopic]);
// Sort the array on context id
for i := size-1 DownTo 1 do
for j := 2 to i do
if (aMyTopicRecords[j-1].idContextHelp > aMyTopicRecords[j].idContextHelp) Then
begin
temp := aMyTopicRecords[j-1];
aMyTopicRecords[j-1] := aMyTopicRecords[j];
aMyTopicRecords[j] := temp;
end;
// Rebuild the original array of topic ids
for iTopic := 0 to size - 1 do
aTopics[iTopic] := aMyTopicRecords[iTopic].idTopic;
end;
The procedure gets called in a loop of the parent function (code snipped):
function GetKeywordsAsHtml(): string;
var
aKeywordList: THndKeywordsInfoArray;
aAssociatedTopics: array of string;
nBlocLevel, nDif, nClose, nCurKeywordLevel, nCurKeywordChildrenCnt: Integer;
nCurKeyword, nCurKeywordTopic: Integer;
nCountAssociatedTopics: Integer;
sCurrentKeyword, sKeywordLink, sKeywordRelated: string;
sKeywordJsCaption: string;
begin
Result := '<ul>';
nBlocLevel := 0;
try
aKeywordList := HndKeywords.GetKeywordList(False);
for nCurKeyword := 0 to length(aKeywordList) - 1 do
begin
sCurrentKeyword := aKeywordList[nCurKeyword].id;
nCurKeywordLevel := HndKeywords.GetKeywordLevel(sCurrentKeyword);
nCurKeywordChildrenCnt := HndKeywords.GetKeywordDirectChildrenCount(sCurrentKeyword);
sKeywordLink := '#';
sKeywordRelated := '[]';
aAssociatedTopics := HndTopicsKeywords.GetTopicsAssociatedWithKeyword(sCurrentKeyword);
nCountAssociatedTopics := Length(aAssociatedTopics);
if nCountAssociatedTopics > 0 then
begin
GetSortedTopicIDs(aAssociatedTopics, nCountAssociatedTopics);
// Code snipped
end;
end;
finally
Result := Result + '</ul>';
end;
end;
The script compiled in the HelpNDoc internal editor with no issues. But when I go to actually build my HTML documentation I encounter a problem:
The HelpNDoc API is explained here.
Is there something wrong with my code?
I decided to go about it a different way and used a simpler technique:
procedure GetSortedTopicIDs(var aTopics : array of String; iNumTopics : Integer);
var
iTopic : Integer;
// List of output
aList: TStringList;
begin
// Init list
aList := TStringList.Create;
// Build a new array of "nnn x"
// - nnn is the help context id
// - x is the topid id
// Note: I know that the context ID values are within the range 0 - 200
for iTopic := 0 to iNumTopics - 1 do
// We pad the context id with 0. We could increase the padding width to
// make the script mre useful
aList.Add(Format('%0.3d %s', [
HndTopics.GetTopicHelpContext(aTopics[iTopic]),
aTopics[iTopic]
]));
// Now we sort the new array (which basically sorts it by context id)
aList.Sort;
// Update original array
for iTopic := 0 to iNumTopics - 1 do
// We ignore the "nnn " part of the string to get just the topic id
aTopics[iTopic] := copy(aList[iTopic],5, length(aList[iTopic])-4);
// Tidy up
aList.Free;
end;
This compiles and I get the sorted array of topic IDs at the end of it. So the pop-up help is now listed as I want.

Only display fields selected in TCheckListBox in a DBGrid via SQL

So I have a TDBGrid that displays the content of a Query via SQL.
I need to be able to only show the fields/columns that are selected in a TCheckListBox. How do I go about this problem?
In this case the 'Lengte' field should not be included'
The columns link back to the datasource so you can iterate over them until you find the one you want.
for cnt := 0 to DBGrid1.Columns.Count -1 do
if DBGrid1.Columns[cnt].FieldName = 'Lengte'
then DBGrid1.Columns[cnt].Visible := false;
Got it to work!
var
i, j: integer;
arrColsToHide: array[0..11] of string;
begin
// resets all columns to VISIBLE and clears array
for i := 0 to 11 do
begin
arrColsToHide[i]:= '';
dbGridExport.Columns[i+1].Visible:= true;
end;
// if item is not checked, add it to the array
for i := 0 to 11 do
if not(clbFieldsToExport.Checked[i])
then arrColsToHide[i]:= clbFieldsToExport.Items.Strings[i];
// compares value of array to fieldname, and hide if same
for j := 0 to 11 do
begin
for i := 1 to dbGridExport.Columns.Count-1 do
if dbGridExport.Columns[i].FieldName = arrColsToHide[j]
then dbGridExport.Columns[i].Visible := false;
end;
end;

Why I cannot find window?

I use this example to send a string between two applications.
When I press the Send button for the first time, the string is sent to the Receiver, but only a part of the string is received.
When I press the Send button for the second time, I get "Window not found!".
The window is right there on screen. Why it works when I press the button the first time, but not the second time?
This is the sender:
procedure TfrmSender.SendString;
var
stringToSend : string;
copyDataStruct : TCopyDataStruct;
begin
Caption:= 'Sending';
stringToSend := 'About - Delphi - Programming';
copyDataStruct.dwData := 12821676; //use it to identify the message contents
copyDataStruct.cbData := 1 + Length(stringToSend) ;
copyDataStruct.lpData := PChar(stringToSend);
SendData(copyDataStruct) ;
end;
procedure TfrmSender.SendData(CONST copyDataStruct: TCopyDataStruct);
VAR
receiverHandle : THandle;
res : integer;
begin
receiverHandle := FindWindow(PChar('TfrmReceiver'), PChar('frmReceiver')) ;
if receiverHandle = 0 then
begin
Caption:= 'Receiver window NOT found!';
EXIT;
end;
res:= SendMessage(receiverHandle, WM_COPYDATA, Integer(Handle), Integer(#copyDataStruct));
if res= 0 then Caption:= 'Receiver window found but msg not hand';
end;
And this is the receiver:
procedure TfrmReceiver.WMCopyData(var Msg: TWMCopyData);
VAR
s : string;
begin
if Msg.CopyDataStruct.dwData = 12821676 then
begin
s := PChar(Msg.CopyDataStruct.lpData);
msg.Result := 2006; //Send something back
Winapi.Windows.Beep(800, 300);
Caption:= s;
end
end;
To summarize the comments there are two errors
1) (See #Tom Brunberg) is that the length is set incorrectly which is why you only get part (about half? of the string)
It should be
copyDataStruct.cbData := sizeof( Char )*(Length(stringToSend) + 1 );
2) The forms caption is being changed which invalidates the expression
FindWindow(PChar('TfrmReceiver'), PChar('frmReceiver'))
because the second parameter is the form's caption (in Delphi terminology)

How to Sort Sections on TMemIniFile

I am using TMemIniFile to store configuration and I need to sort the sections in alpha order.
For that I have created a descendant of TMemIniFile
TRWStudioMemIniFile = class(TMemIniFile)
public
procedure UpdateFile; override;
procedure GetSortedStrings(List: TStrings);
end;
{ TRWStudioMemIniFile }
procedure TRWStudioMemIniFile.GetSortedStrings(List: TStrings);
var
I, J: Integer;
Strings: TStrings;
begin
List.BeginUpdate;
try
Sections.Sort;
for I := 0 to Sections.Count - 1 do
begin
List.Add('[' + Sections[I] + ']');
Strings := TStrings(Sections.Objects[I]);
for J := 0 to Strings.Count - 1 do List.Add(Strings[J]);
List.Add('');
end;
finally
List.EndUpdate;
end;
end;
procedure TRWStudioMemIniFile.UpdateFile;
var
List: TStringList;
begin
List := TStringList.Create;
try
GetSortedStrings(List);
List.SaveToFile(FileName, Encoding);
finally
List.Free;
end;
end;
but it needs to have access to the Sections (actually FSections: TStringList, that is a private member of TMemIniFile)
I have created a Helper class to expose that member thru a property. However this behavior is not supported anymore in Delphi 10.1
I started copy/paste the TMemIniFile to my unit and after and endless process I am ending up making a copy of the entire System.IniFile, just to access the FSections.
My question is how to access that FSections member without need to duplicate everything from that unit just to gain visibility
OR is there another way that I can Sort the Sections before saving? (I am just calling the TStringList.Sort from FSections)
Rather than relying on type-casting and "cracking open" the private member, you can instead get the sections into your own TStringList using the inherited ReadSections() method, sort that list as needed, and then use the inherited ReadSectionValues() method to read the strings for each section:
var
sections: TStringList;
values: TStringList;
begin
sections := TStringList.Create;
try
ReadSections(sections);
sections.Sort;
values := TStringList.Create;
try
List.BeginUpdate;
try
for I := 0 to sections.Count - 1 do
begin
List.Add('[' + sections[I] + ']');
values.Clear; // Just in case
ReadSectionValues(sections[i], values);
for J := 0 to values.Count - 1 do
List.Add(values[J]);
List.Add('');
end;
finally
List.EndUpdate;
end;
finally
values.Free;
end;
finally
sections.Free;
end;
end;

How to get the height and width of the visible browser area

How can I retrieve the height and width of the browser area?
I want to show one form always in landscape mode, regardless whether the mobile phone has rotated the browser view.
if the browser is in portrait postion I want to rotate the form (TForm.Angel := 90). In order to force landscape mode.
Update:
Here is a picture, how the result should look like:
I found a solution, but I am not so really happy with it. It's easy to rotate the view, but the origin is not in the center, so I have to manually correct it, and I don't understand why this transformation is neccessary.
Here is the code:
procedure TForm1.ForceLandscape(aEnabled: Boolean);
var
browserWidth, browserHeight: Integer;
isLandscapeMode: Boolean;
begin
browserHeight := Application.Display.ClientHeight; //BrowserAPI.Window.innerHeight;
browserWidth := Application.Display.ClientWidth; //BrowserAPI.Window.innerWidth;
isLandscapeMode := browserHeight > browserWidth;
if aEnabled and isLandscapeMode then
begin
Angle := 90;
Height := browserWidth;
Width := browserHeight;
end
else
begin
Angle := 0;
Height := browserHeight;
Width := browserWidth;
end;
end;
procedure TForm1.InitializeForm;
begin
inherited;
// this is a good place to initialize components
//Need to put a transform.orign for form rotation (Angle)
var x := trunc(Application.Display.ClientWidth / 2);
var myStyle := TInteger.ToPxStr(x) + ' ' + TInteger.ToPxStr(x);
w3_setStyle(Handle, w3_CSSPrefix('TransformOrigin'), myStyle);
end;
The simplest way would be to use the main form's With and Height.
Create a new "Visual project" and add an EditBox to the form.
Put this code into the "Resize" method:
Edit1.Text := Format('%d x %d', [Self.Width, Self.Height]);
If you, however, want to keep the main-form at a fixed size, you would need to read some other properties.
Self.Width := 250;
Self.Height := 250;
There are several ways to get these dimensions. Both the "window" and "document" DOM-element have some properties you can use:
window.innerWidth
document.documentElement.clientWidth
document.body.clientWidth
In Smart you can access these from the Application object:
(Add two more edit-boxes...)
W3EditBox2.Text := Format('%d x %d', [Application.Document.ClientWidth, Application.Document.ClientHeight]);
W3EditBox3.Text := Format('%d x %d', [Application.Display.ClientWidth, Application.Display.ClientHeight]);
It seems to be a bug in Application.Document.ClientHeight as it always returns 0...
Remember that the running code is JavaScript. You can find lots of useful information on the web.
It's very easy to transform these JS-snippets into Smart Pascal via an asm section.
Eg:
var
w,h: Integer;
begin
//Pure JavaScript below.
//The #-prefix maps the variable to a SmartPascal declared variable
asm
#w = window.innerWidth;
#h = window.innerHeight;
end;
W3EditBox4.Text := Format('%d x %d', [w, h]);
end;
Another trick would be to declare a Variant variable.
A Variant variable can hold a whole JavaScript object, and enable "late binding"-ish access to the members of the object:
var
v: Variant;
begin
//Grab the whole "document" element from the DOM
asm
#v = document;
end;
//Access all "document" members directly via the "v"-variable
W3EditBox5.Text := Format('%d x %d', [v.documentElement.clientWidth, v.documentElement.clientHeight]);
end;
Screenshot of the code-snippets above: