Jump to content

Leaderboard


Popular Content

Showing content with the highest reputation since 02/05/2019 in all areas

  1. 7 points
    jarto

    TW3StringGrid is available

    A new update is available in the development-channel of SmartUpdate. It contains a completely new Grid: TW3StringGrid. It's a completely new design, which was made to be very fast and able to handle lots of data. This is achieved through dynamic drawing: Only the visible grid lines are rendered. There is a new StringGrid-demo in the Featured Demos. It downloads a json file and populates the grid with data from it. TW3StringGrid supports six different column types: Text (column class TW3StringGridTextColumn) Numeric (column class TW3StringGridNumericColumn) EditBox (column class TW3StringGridEditColumn) ComboBox (column class TW3StringGridComboColumn) Checkmark (column class TW3StringGridCheckmarkColumn) Button (column class TW3StringGridButtonColumn) Columns are created by calling: MyNewColumn:=Grid.AddColumn(ColumnClass); If no ColumnClass is given, a Text column is created. Column properties: Caption Width BorderType Default is btLightBorderRight. Set to btNone if you don't want any vertical borders. Backgroundtype Default is btNone to let the line color through. AlignText (The same way as in TW3Label) taLeft (default) taCenter taRight SelectOptions (for TW3StringGridComboColumn only) This is an array of selectable values. First value (index 0) is the value for no selection. For example: SelectOptions:=['','First','Second','Third']; RowBorderType: Default is btLightBorderBottom. Set to btNone if you don't want any horizontal borders. RowBackgroundType: Default is bsListItemBackground. Set to bsDecorativeListItemBackground (or test other backgrounds) to change the background style for even rows. RowOddBorderType: Default is btLightBorderBottom. Set to btNone if you don't want any horizontal borders. Affects odd rows. RowOddBackgroundType: Default is bsListItemBackground. Set to bsDecorativeListItemBackground (or test other backgrounds) to change the background style for odd rows. Grid properties: RowCount: Set number of rows to show FixedColumns: How many columns should be fixed (aka not scrollable horizontally) LineHeight MultiSelect Events: OnCellClick: Is triggered when the row is clicked. OnCellChanged: Is triggered when cell content changes through editing (for example: through editing) OnDrawGridLineTheme: Can be used to set custom backgrounds and borders for a row. (for example, set background to bsErrorBackground for lines that should be highlighted to the user) Methods: InvalidateGrid: Triggers a repaint of the grid. Sort Sorting TW3StringGrid supports sorting based on one of multiple columns. Clicking on the column headers sets or reverses sort order. You can also control sorting in code: procedure AddSortColumn(Index: Integer; SortOrder: TW3SortOrder = soNormal); overload; procedure AddSortColumn(Col: TW3StringGridColumn; SortOrder: TW3SortOrder = soNormal); overload; procedure SetSortColumn(Index: Integer; SortOrder: TW3SortOrder = soNormal); overload; procedure SetSortColumn(Col: TW3StringGridColumn; SortOrder: TW3SortOrder = soNormal); overload; procedure ToggleSortColumn(Index: Integer); For example: Grid.SortOptions.AddSortColumn(3,soReverse); //Set primary sort column Grid.SortOptions.AddSortColumn(1); //Add secondary sort columns Grid.Sort; Working with sorted data When the Grid is sorted, it only affects how data is shown. Sorting does not change Grid data itself at all. When you work with a sorted grid events and indexes (for example SelectedIndex) always refer to the index in the grid data itself and NOT the visible line number. TW3StringGrid uses these events: TW3StringGridColumnEvent = procedure(const Sender: TW3StringGridColumn; const Row: Integer); TW3StringGridEvent = procedure(const Sender: TObject; const Row, Col: Integer); If you have a Grid where "Australia" is on line 2, SelectedIndex:=2 selects that row regardless of how the grid is sorted. Clicking on that row or changing data on that row also returns Row=2 regardless of how the grid is sorted.
  2. 4 points
    Czar

    UI layout

    For me the most frustrating aspect of using SMS is layouts. Putting together anything beyond a trivial UI takes ages and is very frustrating to achieve. The Tlayout is not intuitive, it has weird and unexplainable side effects and takes a long time to get it right. Once you have it working then generally it is fine. We really need to have a more intuitive system to allow easy to create and maintain UI. I believe simple anchor (left, top, right, bottom)m, Align and "alignWithMargin" like Delphi has would be a great bonus to SMS. I have no idea what would be involved, or how much work it would be, but I would love to see this being developed.
  3. 4 points
    jarto

    Beware of box shadows!

    I have been working on a new TW3StringGrid for Smart Mobile Studio. The original plan was to include it in 3.0, but there was a problem: It was slow on iOS. So, ever since the release, I've tried to crack this problem. How can Javascript that works beautifully on every other platform and browser be absolutely horrible on iOS? It was extremely fast on desktop browsers and Android tablets. On iOS it took seconds to draw the grid and scrolling it was close to unusable. After countless of hours of testing and trying, I finally found the reason: Box shadows in CSS. For example: .TW3ContainerBorder { border-radius: <?pas=EdgeRounding?>; border-top: 1px solid rgba(250, 250, 250, 0.7); border-left: 1px solid rgba(250, 250, 250, 0.7); border-right: 1px solid rgba(240, 240, 240, 0.5); border-bottom: 1px solid rgba(240, 240, 240, 0.5); -webkit-box-shadow: 0px 0px 1px 1px <?pas=GetRGBA(clDlgShadowBaseColor, 0.3)?>; -moz-box-shadow: 0px 0px 1px 1px <?pas=GetRGBA(clDlgShadowBaseColor, 0.3)?>; -ms-box-shadow: 0px 0px 1px 1px <?pas=GetRGBA(clDlgShadowBaseColor, 0.3)?>; -o-box-shadow: 0px 0px 1px 1px <?pas=GetRGBA(clDlgShadowBaseColor, 0.3)?>; box-shadow: 0px 0px 1px 1px <?pas=GetRGBA(clDlgShadowBaseColor, 0.3)?>; } .TW3FlatBorder { border-radius: <?pas=EdgeRounding?>; border-top: 1px solid rgba(44, 44, 44, 0.8); border-left: 1px solid rgba(44, 44, 44, 0.8); border-right: 1px solid rgba(44, 44, 44, 0.8); border-bottom: 1px solid rgba(44, 44, 44, 0.8); } I had used a TW3ContainerBorder for every cell in the grid. Replacing them with a flat border improved performance dramatically on iOS. Something like an order of magnitude faster. So if you've been having performance problems with iOS, that's most likely the reason.
  4. 4 points
    lynkfs

    scrolling big numbers

    Got challenged the other day by trying to scroll large numbers of rows in a listbox. Browsers are very good at scrolling, but when row numbers become large and the scroll context becomes complex, scroll behaviour deteriorates. A simple un-optimised scrolling div is, rule of thumb, able to scroll comfortably up to a couple of hundred rows. Depending on browser and complexity. In the native framework I tweaked that a bit by setting all rows outside the visible viewport to 'display:none'. That extends comfortable scrolling somewhat, say to numbers in the low thousands. A better way is to completely separate display from content. Meaning that the content needs to be held in some kind of memory structure, not in visual components, and that scrolling is redefined to re-using a small set of visual rows confined to the scroll-window. A structure to make that happen would be a <div> like component (TW3Panel) on a form with NativeScrolling set to true. Set up 2 child-panels on this component, where the first child-panel has dimensions equal to the maximum scrollable area, and a second childpanel dimensioned equal to this parent. The first child-panel is the scroller, which will always be completely empty. The second child-panel contains the visible rows, which will be refreshed on scroll-events. To keep this panel always in view, set the position attribute to '-webkit-sticky'. This demo shows that Chrome can readily handle 500,000 rows, even on mobile. FF a bit less. The good thing is that the number of rows doesn't really matter, scrolling behaviour stays the same regardless. Would be interesting to see if performance gets even better by pushing scrolling to the GPU, pretty sure Chrome does that by default. Infinite scrolling will be easy to implement using this structure too. Code procedure TForm1.W3Button1Click(Sender: TObject); begin //data var column1 := TVariant.CreateArray; for var i := 0 to 500000 do begin column1.push(inttostr(i)); end; var column2 := TVariant.CreateArray; for var i := 1 to 500001 do begin column2.push(inttostr(i)); end; //column2.sort(); //sorting works too //scrolling setup var rowHeight := 30; W3Panel.NativeScrolling := true; //Parent panel (in visual designer : width 408, height 266) var Panel1 : TW3Panel := TW3Panel.Create(W3Panel); //scroller (always empty!) Panel1.SetBounds (0,0,380,column1.length*rowHeight); //but with large height Panel1.BorderRadius := 0; //set up viewport var Panel2 : TW3Panel := TW3Panel.Create(W3Panel); //viewport, only the visual rows Panel2.SetBounds(0,0,380,262); //dimensions same as grid-parent Panel2.BorderRadius := 0; Panel2.handle.style.position := '-webkit-sticky'; Panel2.handle.style.position := '-moz-sticky'; Panel2.handle.style.position := '-ms-sticky'; Panel2.handle.style.position := '-o-sticky'; Panel2.handle.style.position := 'sticky'; Panel2.handle.style.top := '0px'; //set up columns Var CPanel1 : TW3Panel := TW3Panel.Create(Panel2); //column 1 CPanel1.SetBounds(-2,-2,200,264); CPanel1.BorderRadius := 0; Var CPanel2 : TW3Panel := TW3Panel.Create(Panel2); //column 2 CPanel2.SetBounds(200,-2,200,264); CPanel2.BorderRadius := 0; //initial fill viewport prior to first onscroll event for var j := 0 to 8 do begin //only first couple of rows var x : TW3Panel := TW3Panel.Create(CPanel1); //column 1 x.SetBounds(-2,j*rowHeight-2,200,rowHeight); x.BorderRadius := 0; x.innerHTML := column1[j]; x.onclick := procedure(sender:TObject) begin showmessage((sender as TW3Panel).innerHTML); end; // var y : TW3Panel := TW3Panel.Create(CPanel2); //column 2 y.SetBounds(-2,j*rowHeight-2,190,rowHeight); y.BorderRadius := 0; y.innerHTML := column2[j]; y.onclick := procedure(sender:TObject) begin showmessage((sender as TW3Panel).innerHTML); end; end; //fill viewport while scrolling var c1 := CPanel1.handle.children; var c2 := CPanel2.handle.children; W3Panel.onscroll := procedure(sender:tobject) begin var d : integer := trunc(W3Panel.handle.scrollTop/rowHeight); for var k := 0 to 8 do begin c1[k].innerHTML := column1[d+k]; c2[k].innerHTML := column2[d+k]; end; //fall back : ide chrome browser and iOS don't handle 'sticky' very well, //in that case set viewport position manually if Panel2.handle.style.position <> 'sticky' then if not w3_getIsSafari then Panel2.top := W3Panel.handle.scrollTop; end; end; infinity scroll : add to end of W3Panel.onscroll procedure //infinity scroll : if W3Panel.handle.scrollHeight - W3Panel.handle.scrollTop = W3Panel.handle.clientHeight then W3Panel.handle.scrollTop := 0;
  5. 4 points
    jarto

    Update embedded Chromium

    Hi guys, I've been a busy for a while working on updating the Embedded Chrome we use in the IDE. The one we've used so far has been from Henri Gourvest. Unfortunately it's not been updated for 3 years, but there's a new fork by Salvador Diaz Fau, which is actively updated: https://github.com/salvadordf/CEF4Delphi
  6. 4 points
    warleyalex

    async / await keywords

    look this following procedure: procedure main; begin Sleep(5000); console.log('A'); Sleep(3000); console.log('B'); end; when we run this code, it will immediately output 'A' and 'B' - JS the asyncronous nature. ...but I want our program to log 'A' after 5 seconds then log 'B' after 3 seconds... that why we, we need the async / await native keywords in SMS. In a "synchronous" manner. Await and Async are just syntactic sugar for working with promises. It would be cool if we can declare a method like: procedure main; async; The compiler can check whether the encompassing function has async() in then and emit expectd JS, for instance: (async function(){ })(); Yeah, we need to to wrap it inside an async function and add "await" before the method "Sleep" to work in synchrounous manner.ther await keyword native in SMS would be nice. await Sleep(5000); I found out an ugly workaround for the "Immediately Invoked Function Expression (IIFE) - async function" to work at SMS. { global functions } procedure async(fn: Variant = nil); external '(async function(){' ; procedure &end; external '})'; function await(promise: Variant): JPromise; external 'await '; function Sleep(ms: Integer): JPromise; begin Result := JPromise.Create( procedure(resolve,reject : TCallBack) begin window.setTimeout( procedure() begin console.log('Done waiting'); resolve(ms); end, ms); end); procedure main; begin async; await( Sleep(5000) ); console.log('A after 5s'); await (Sleep(3000)); console.log('B after 3s'); &end; end; end; the compiler will emit expected JS at least: function main() { (async function(){(null); await (Sleep$1(5000)); console.log("A after 5s"); await (Sleep$1(3000)); console.log("B after 3s"); })(); }; but it would super awesome if we could // define procedure as async function procedure main: async; procedure main; begin await Sleep(5000); console.log('A after 5s'); await Sleep(3000); console.log('B after 3s'); end;
  7. 3 points
    lynkfs

    visual processing

    A while ago I posted some ideas and a demo on the subject of 'nocode' or 'locode' development. The idea was to do a domain modelling exercise, and extract or generate an application from that with no or minimal coding required. The demo put a couple of the proposed models through its paces. After that post I put the subject back in the incubator for a bit. Sort of recently I realised that having an rtl with visual components and an ide with a form painter is a good thing to have, but only covers part of what is necessary if we need to cover process logic, or business rules, without having to resort to coding. Various computing packages (datamining, visual analytics etc) model business logic by providing specialised components, which can be strung together and parametrised. I tried this approach out (using KNIME) with the following (nonsensical) example process : "from the NorthWind database get all companies, and from the FishFacts database extract all species, collate only those entries from both datasources where the name starts with the letter 'L' and list them. Also produce a pie-chart of all fish species showing their length in cm." This (nonsense) process translates to the node structure above. Nodes typically can be selected from a list of available nodes and right-clicking gives parametrisation forms, f.i. the MYSQL connector asks for a host, database name, credentials etc. Stringing these components together is a simple process in itself, and the output produces something like Not too bad. I've coded a couple of these type of components in Smart, which works really well. For the technical architecture I've based these on the principles of FBP (flow based programming) where every node is a webworker, and the connectors between these nodes/webworkers are defined by channel-objects. Data transfer between these nodes/channels is done by messaging. See post here. It would be nice to extend the rtl with these types of components, including a process painter
  8. 3 points
    jarto

    Development updates

    A new update is available. This one contains a completely new Grid: TW3StringGrid. It's a completely new design, which was made to be very fast and able to handle lots of data. This is achieved through dynamic drawing: Only the visible grid lines are rendered. There is a new StringGrid-demo in the Featured Demos. It downloads a json file and populates the grid with data from it. RTL: New control: TW3StringGrid New featured demo for the StringGrid. New TW3CustomScrollControl, for developing scroll controls with scrollbars/indicators-support. New border types: btLightBorderTop, -Bottom, -Left, -Right, -Horiz and -Vert EventManager: Check TouchPreventDefault before trying to prevent extra artificial mouse events from touch events Improvements to TW3BlockBox and Modal dialogs: TW3BlockBox did not trigger a resize Dialogs did not get resized when window was resized or device flipped Make sure the dialogs are not too big
  9. 3 points
    DavidRM

    Chat Scroller - Need Some Tips

    I built this component to use in my Paintball Net game UI: unit UPBTInfoScroll; interface uses System.Types, System.Lists, System.Time, System.Colors, SmartCL.System, SmartCL.Theme, SmartCL.Components, SmartCL.Controls.Label, SmartCL.Scroll, SmartCL.Controls.ScrollBar, SmartCL.Css.Classes; type TPBTScrollItem = class(TW3CustomControl) protected FItemText: string; procedure InitializeItem; virtual; procedure SetItemText(aValue: string); public function CreationFlags: TW3CreationFlags; override; procedure InitializeObject; override; procedure UpdateDisplay; virtual; property ItemText: string read FItemText write SetItemText; end; TPBTScrollItemClass = class of TPBTScrollItem; TPBTScrollInfoItem = class(TPBTScrollItem) protected public procedure UpdateDisplay; override; property InfoText: string read ItemText write ItemText; end; TPBTScrollErrorItem = class(TPBTScrollInfoItem) end; TPBTChatType = (ctAnnounce, ctChat, ctSay, ctShout, ctWhisper, ctTell, ctPlan, ctTeamPlan, ctWizChat); TPBTScrollChatItem = class(TPBTScrollItem) private FChatType: TPBTChatType; FChatHeader: string; FChatText: string; FChatColor: TColor; public procedure UpdateDisplay; override; property ChatTYpe: TPBTChatType read FChatType; property ChatHeader: string read FChatHeader; property ChatText: string read FChatText; property ChatColor: TColor read FChatColor; end; TPBTScrollItems = class(TW3ScrollContent) private function GetItem(Index: Integer): TPBTScrollItem; function GetCount: integer; public function Add: TPBTScrollItem; overload; function Add(aScrollItemClass: TPBTScrollItemClass): TPBTScrollItem; overload; procedure Clear; virtual; procedure FinalizeObject; override; procedure Delete(aIndex: integer); published property Items[index: Integer]: TPBTScrollItem read GetItem; default; property Count: integer read GetCount; end; TPBTInfoScrollAddItemCallback = function: TPBTScrollItem; TPBTInfoScroll = class(TW3ScrollControl) private function GetItems: TPBTScrollItems; function GetScrollIsAtBottom: boolean; protected procedure InitializeObject; override; procedure Resize; override; function GetScrollContentClass: TW3ScrollContentClass; override; public function CreationFlags: TW3CreationFlags; override; procedure Clear; procedure ScrollToBottom; procedure RealignItems; procedure AddItem(aCallBack: TPBTInfoScrollAddItemCallback); procedure AddText(const aText: string); procedure AddChat(aChatType: TPBTChatType; const aChatHeader, aChatText: string; aChatColor: TColor); procedure AddInfo(const aInfoText: string); procedure AddError(const aErrorText: string); function MaxInfoWidth: integer; function MinInfoWidth: integer; published property Items: TPBTScrollItems read GetItems; property ScrollIsAtBottom: boolean read GetScrollIsAtBottom; end; implementation // TPBTScrollItem procedure TPBTScrollItem.InitializeItem; begin end; procedure TPBTScrollItem.SetItemText(aValue: string); begin if aValue <> FItemText then begin FItemText := aValue; UpdateDisplay; end; end; function TPBTScrollItem.CreationFlags: TW3CreationFlags; begin inherited; Include(Result, cfAllowSelection); end; procedure TPBTScrollItem.InitializeObject; begin inherited; SetContentSelectionMode(tsmText); end; procedure TPBTScrollItem.UpdateDisplay; begin InnerHTML := Format('<span style="user-select:text;-moz-user-select:text;-webkit-user-select:text;-ms-user-select:text;-khtml-user-select:text">%s</span>', [TString.EncodeTags(FItemText)]); end; // TPBTScrollInfoItem procedure TPBTScrollInfoItem.UpdateDisplay; begin inherited; end; // TPBTScrollErrorItem // TPBTScrollChatItem procedure TPBTScrollChatItem.UpdateDisplay; begin inherited; InnerHTML := Format('<span style="color:%s;user-select:text;-moz-user-select:text;-webkit-user-select:text;-ms-user-select:text;-khtml-user-select:text"><b>%s</b>: %s</span>', [ColorToWebStr(FChatColor), TString.EncodeTags(FChatHeader), TString.EncodeTags(FChatText)]); end; // TPBTScrollItems function TPBTScrollItems.GetItem(Index: Integer): TPBTScrollItem; begin Result := TPBTScrollItem(GetChildren[Index]); end; function TPBTScrollItems.GetCount: integer; begin Result := GetChildCount; end; function TPBTScrollItems.Add: TPBTScrollItem; begin Result := Add(TPBTScrollItem); end; function TPBTScrollItems.Add(aScrollItemClass: TPBTScrollItemClass): TPBTScrollItem; begin Result := aScrollItemClass.Create(Self); Result.InitializeItem; end; procedure TPBTScrollItems.Clear; begin // can't get rid of from [0]. Not sure why. while Count > 0 do GetChildren[Count - 1].Free; Height := 0; end; procedure TPBTScrollItems.FinalizeObject; begin Clear; inherited; end; procedure TPBTScrollItems.Delete(aIndex: integer); begin if aIndex < GetChildCount then GetChildren[aIndex].Free; end; // TPBTInfoScroll function TPBTInfoScroll.GetItems: TPBTScrollItems; begin Result := TPBTScrollItems(Content); end; function TPBTInfoScroll.GetScrollIsAtBottom: boolean; begin Result := (ScrollController.ContentTop <= -(Content.Height - (Height + 10))) end; procedure TPBTInfoScroll.InitializeObject; begin inherited; SetBarSize(CNT_SCROLLBAR_SIZE); ScrollBars := sbScrollBar; end; procedure TPBTInfoScroll.Resize; begin inherited; end; function TPBTInfoScroll.GetScrollContentClass: TW3ScrollContentClass; begin Result := TPBTScrollItems; end; function TPBTInfoScroll.CreationFlags: TW3CreationFlags; begin inherited; // Allow key-capture and selection include(result, cfKeyCapture); include(result, cfAllowSelection); end; procedure TPBTInfoScroll.Clear; begin Items.Clear; SetSize(ClientWidth, 0); ScrollController.Refresh; end; procedure TPBTInfoScroll.ScrollToBottom; begin ScrollController.ScrollTo(0, -(Content.Height - Height)); end; procedure TPBTInfoScroll.RealignItems; var ii: integer; begin ScrollController.Refresh; if Items.Count > 0 then begin Items.BeginUpdate; try Items[0].Top := 0; for ii := 1 to Items.Count - 1 do begin Items[ii].Top := Items[ii - 1].Top + Items[ii - 1].Height; end; finally Items.EndUpdate; end; end; end; procedure TPBTInfoScroll.AddItem(aCallBack: TPBTInfoScrollAddItemCallback); const margin = 5; MAX_ITEMS = 500; var lastItem, aItem: TPBTScrollItem; needRealign, wasAtBottom: boolean; begin Items.BeginUpdate; Content.BeginUpdate; try if Items.Count > 0 then lastItem := Items[Items.Count - 1]; needRealign := Items.Count > MAX_ITEMS; if needRealign then while Items.Count > (MAX_ITEMS div 3) * 2 do Items.Delete(0); if Content.Width <> (ClientWidth - GetBarSize) then Content.Width := (ClientWidth - GetBarSize); wasAtBottom := (ScrollController.ContentTop <= -(Content.Height - (Height + 10))); aItem := aCallBack; aItem.UpdateDisplay; if lastItem <> nil then aItem.Top := lastItem.Top + lastItem.Height else aItem.Top := 0; Content.Height := aItem.Top + aItem.Height + margin; if Content.Height <= Height then begin Content.Height := Height; wasAtBottom := True; end; finally Content.EndUpdate; Items.EndUpdate; end; if needRealign then RealignItems; ScrollController.Refresh; if wasAtBottom then ScrollToBottom; if needRealign then Invalidate; end; procedure TPBTInfoScroll.AddText(const aText: string); begin AddItem( lambda Result := Items.Add; Result.ItemText := aText; end ); end; procedure TPBTInfoScroll.AddChat(aChatType: TPBTChatType; const aChatHeader, aChatText: string; aChatColor: TColor); begin AddItem( lambda Result := Items.Add(TPBTScrollChatItem); TPBTScrollChatItem(Result).FChatType := aChatType; TPBTScrollChatItem(Result).FChatHeader := aChatHeader; TPBTScrollChatItem(Result).FChatText := aChatText; TPBTScrollChatItem(Result).FChatColor := aChatColor; end ); end; procedure TPBTInfoScroll.AddInfo(const aInfoText: string); begin AddItem( lambda Result := Items.Add(TPBTScrollInfoItem); TPBTScrollInfoItem(Result).InfoText := aInfoText; end ); end; procedure TPBTInfoScroll.AddError(const aErrorText: string); begin AddItem( lambda Result := Items.Add(TPBTScrollErrorItem); TPBTScrollErrorItem(Result).InfoText := aErrorText; end ); end; function TPBTInfoScroll.MaxInfoWidth: integer; var aMax: string; begin while aMax.Length < 70 do aMax += 'XXXXXXXXXX'; Result := MeasureText(aMax).tmWidth + GetBarSize; end; function TPBTInfoScroll.MinInfoWidth: integer; begin Result := MaxInfoWidth div 2; end; end. My goal was to have it act like a scrolling chat. New items are added to the bottom, pushing up the items already there. By default scrolling "sticks" to the bottom of the view area, unless the player has manually scrolled it back to review something or whatever. Overall, it works. What I'm looking for are missed opportunities and maybe some solutions to a couple issues: The biggest issue is the build up of items in the scroll. Right now, I have it check for a maximum size, then prune it down to half that size. I would *prefer* if it could be indefinitely large. The solutions I've seen for that have relied on items being the same height. That constraint doesn't work for me. Resizing isn't exactly snappy/smooth. Just seems like there's probably a way to make that better. Also, as a sorta sub-goal, I wanted to share what I had done. SMS needs a lot more sharing going on. 😃 So...hit me. Tell me how I'm doing it wrong and/or could do it better. Thanks! -David
  10. 3 points
    jarto

    Development updates

    New update is available: Bug fixes and improvements to the IDE: BringToFront and SendToBack will now affect component creation order Opening files from Project Search Path is fixed
  11. 2 points
    Back in the mid-90's, my brother created what he called an "action MUD". He called it "Paintball Net". PBN used ANSI text terminal commands to create a combined roll-and-scroll and animated text experience. ! (exclamation points) were trees, _ (underscores) were grass, ^ (up carets) were mountains, and more. Your "avatar" was a "Y" (uppercase y) and enemies were * (asterisks). You used MUD-like commands to move around the world and target your paintball gun and buy and sell and more. I wish I had a screenshot of what the first version looked like, but you'll just have to imagine. 😃 He asked me to create the GUI for the game. That was 1996, and I had just purchased Delphi 2 and wrapped up my first "learn Delphi" project. This sounded like a good next step, and "PBTerm" was born. I *do* have screenshots of that. Oh, yeah. Love those 16-color Windows graphics. 😃 During the years it was online, the game evolved and upgraded to look more like this: We were never going to win any awards, but we had a very devoted player community. 😃 Paintball Net was online from the summer of 1996 through the summer of 2000, when we took it offline to focus on other projects. The game was never huge, but we had thousands of players come through over that period. And since then, every year at least a few of the players have reached out and asked/begged/pleaded/demanded if we were going to put it back online. There really wasn't much chance of the original version going back online. The original server, written in ANSI C for Linux had proven rather fragile, and was a big part of why we took the game offline. It was taking hours every day just to keep it up and running, in addition to time spent managing the community of players. On top of that, a hard drive incident in 2008 had cost me the source code of a number of core third-party/modified components for PBTerm. This past summer, though, I realized I might be able to make the game live again using Smart Mobile Studio. I would do a straight port of the ANSI C server to Smart Pascal using Node.JS and WebSocket, and I would rebuild the PBTerm client as a browser-based client. I'm not going to say it was *easy*, but it has been a lot of fun. ANSI C converts to Pascal without a lot of painful gyrations. And Node.JS seems a LOT more flexible, stable, and powerful than the TCP sockets approach we were using before. Also, game development is a lot easier when you already own all the graphic and audio resources. 😃 This is the server "in action"... Recreating the client has been more complicated. 20+ years of Delphi habits had to be adapted to the new reality of SMS and a browser-based UI. But that's coming together too. It might be obvious, but I'm not targeting this game at mobile. Paintball Net needed a mouse and keyboard in 1996, it's gonna need a mouse and keyboard in 2018. Also, I've made as few modifications to the gameplay as possible. I really wanted to bring back the original as much as I could. Today I got the handful of sound effects integrated, which was easier than I expected. I'm planning to start testing soon. I just need to line up a server to use and find a few volunteers. I'm excited. This would never have happened without Smart Mobile Studio. So I figured I would share. Merry Xmas! -David
  12. 2 points
    lynkfs

    visual processing

    See previous post : any type of no-code / lo-code solution requires a visual ide, not only for the visual components like buttons etc but also for the non-visual process components. Such an ide allows for both visual and non-visual components to be connected according to the principles of FBP. See previous post and link here. To see how this would pan out, I made this proof of concept no-code / lo-code web-based ide. (Proof of concept not being a full fledged finished product, but at least gives some insights) To diminish the strain on the brain a bit, the layout is based on the Smart ide, including the shameless reuse of its icons. Some highlights : - drag and drop placement of components rather than point and select - the usual component move and resize facilities but now including Delphi-type align options - drag and drop fbp connections between components using jsPlumb A quick rundown : 1) drag a button on the design pane 2) select 'Database' from the top tab-control, and drag both a grid and mySQL component on the design pane. Note the property pane is updated in realtime 3) (optionally click-select any dragged component to resize them. Select both grid and button, right-click on either for alignment options) 4) switch to the No-code tab and connect the button to the mySQL component, and the mySQL component to the grid 5) right-click the mySQL component and provide details (user-id, server, passwords etc). Not shown 6) switch to the Preview tab and click the button This project compiles to 63 KB (0.06 M. Not bad Project code here
  13. 2 points
    jarto

    UI layout

    Yes, we need anchors and aligns. Now that I'm about to get TW3StringGrid finished, I'll have a look at Anchors and Align.
  14. 2 points
    Having a cursory view on this library, what you probably want to end up with is a pascal variable called 'client', which you can use outside asm blocks. This will allow you to issue calls like client.getEventList() etc. to do that you can do something like this var client : variant := new JObject; asm @client = new JSONRpcClient({ 'url': '//user-api.simplybook.me', 'headers': { 'X-Company-Login': 'ACME', 'X-Token': token }, 'onerror': function (error) { alert(error); } }); end; var EventList: variant := client.getEventList(-params-); rule of thumb: the creation of js objects via the 'new' constructor need to be kept in an asm block
  15. 2 points
    DavidRM

    Chat Scroller - Need Some Tips

    Here's the overhauled version, that only uses as many actual on-screen elements as required to show the visible items. It's been working pretty well. unit UPBTInfoScroll; interface uses System.Types, System.Lists, System.Time, System.Colors, SmartCL.System, SmartCL.Theme, SmartCL.Components, SmartCL.Controls.Label, SmartCL.Scroll, SmartCL.Controls.ScrollBar, SmartCL.Css.Classes; type TPBTScrollItemData = class(TObject) private FItemText: string; FItemTop: integer; FItemHeight: integer; public function ItemStyleName: string; virtual; function InnerHtml: string; virtual; property ItemText: string read FItemText write FItemText; property ItemTop: integer read FItemTop write FItemTop; property ItemHeight: integer read FItemHeight write FItemHeight; end; TPBTScrollInfoItemData = class(TPBTScrollItemData) public function ItemStyleName: string; override; end; TPBTScrollErrorItemData = class(TPBTScrollItemData) public function ItemStyleName: string; override; end; TPBTChatType = (ctAnnounce, ctChat, ctSay, ctShout, ctWhisper, ctTell, ctPlan, ctTeamPlan, ctWizChat); TPBTScrollChatItemData = class(TPBTScrollItemData) private FChatType: TPBTChatType; FChatHeader: string; FChatColor: TColor; public function ItemStyleName: string; override; function InnerHtml: string; override; property ChatType: TPBTChatType read FChatType write FChatType; property ChatHeader: string read FChatHeader write FChatHeader; property ChatColor: TColor read FChatColor write FChatColor; property ChatText: string read ItemText write ItemText; end; TPBTScrollItem = class(TW3CustomControl) protected FItemData: TPBTScrollItemData; procedure SetItemData(aValue: TPBTScrollItemData); public function CreationFlags: TW3CreationFlags; override; procedure InitializeObject; override; procedure UpdateDisplay; virtual; property ItemData: TPBTScrollItemData read FItemData write SetItemData; end; TPBTScrollItemClass = class of TPBTScrollItem; TPBTScrollInfoItem = class(TPBTScrollItem) end; TPBTScrollErrorItem = class(TPBTScrollInfoItem) end; TPBTScrollChatItem = class(TPBTScrollItem) end; TPBTScrollItems = class(TW3ScrollContent) private function GetItem(Index: Integer): TPBTScrollItem; function GetCount: integer; public function Add: TPBTScrollItem; overload; function Add(aScrollItemClass: TPBTScrollItemClass): TPBTScrollItem; overload; procedure Clear; virtual; procedure FinalizeObject; override; procedure Delete(aIndex: integer); published property Items[index: Integer]: TPBTScrollItem read GetItem; default; property Count: integer read GetCount; end; TPBTInfoScrollAddItemDataCallback = function: TPBTScrollItemData; TPBTInfoScrollAddItemCallback = function: TPBTScrollItem; TPBTInfoScroll = class(TW3ScrollControl) private FDataItems: array of TPBTScrollItemData; FNewItems: array of TPBTScrollItemData; function GetItems: TPBTScrollItems; function GetScrollIsAtBottom: boolean; procedure ScrollControllerScrolling(Sender: TObject); protected procedure InitializeObject; override; procedure Resize; override; function GetScrollContentClass: TW3ScrollContentClass; override; public function CreationFlags: TW3CreationFlags; override; procedure Clear; procedure Refresh; procedure ScrollToBottom; procedure RealignItems; procedure UpdateVisibleItems; procedure AddItem(aCallback: TPBTInfoScrollAddItemDataCallback); procedure AddText(const aText: string); procedure AddChat(aChatType: TPBTChatType; const aChatHeader, aChatText: string; aChatColor: TColor); procedure AddInfo(const aInfoText: string); procedure AddError(const aErrorText: string); function MaxInfoWidth: integer; function MinInfoWidth: integer; published property Items: TPBTScrollItems read GetItems; property ScrollIsAtBottom: boolean read GetScrollIsAtBottom; end; implementation function TPBTScrollItemData.ItemStyleName: string; begin Result := 'TPBTScrollItem'; end; function TPBTScrollItemData.InnerHtml: string; begin Result := Format('<span style="user-select:text;-moz-user-select:text;-webkit-user-select:text;-ms-user-select:text;-khtml-user-select:text">%s</span>', [TString.EncodeTags(FItemText)]); end; function TPBTScrollInfoItemData.ItemStyleName: string; begin Result := 'TPBTScrollInfoItem'; end; function TPBTScrollErrorItemData.ItemStyleName: string; begin Result := 'TPBTScrollErrorItem'; end; function TPBTScrollChatItemData.ItemStyleName: string; begin Result := 'TPBTScrollChatItem'; end; function TPBTScrollChatItemData.InnerHtml: string; begin Result := Format('<span style="color:%s;user-select:text;-moz-user-select:text;-webkit-user-select:text;-ms-user-select:text;-khtml-user-select:text"><b>%s</b>: %s</span>', [ColorToWebStr(FChatColor), TString.EncodeTags(FChatHeader), TString.EncodeTags(ChatText)]); end; // TPBTScrollItem procedure TPBTScrollItem.SetItemData(aValue: TPBTScrollItemData); begin if FItemData <> nil then TagStyle.RemoveClassFromControl(Handle, FItemData.ItemStyleName); FItemData := aValue; if FItemData <> nil then begin Top := FItemData.ItemTop; TagStyle.AddClassToControl(Handle, FItemData.ItemStyleName); end; UpdateDisplay; end; function TPBTScrollItem.CreationFlags: TW3CreationFlags; begin inherited; Include(Result, cfAllowSelection); end; procedure TPBTScrollItem.InitializeObject; begin inherited; SetContentSelectionMode(tsmText); w3_setStyle(Handle, 'min-width', '100%'); end; procedure TPBTScrollItem.UpdateDisplay; begin if FItemData <> nil then InnerHTML := FItemData.InnerHtml else InnerHTML := ''; end; // TPBTScrollInfoItem // TPBTScrollErrorItem // TPBTScrollChatItem // TPBTScrollItems function TPBTScrollItems.GetItem(Index: Integer): TPBTScrollItem; begin Result := TPBTScrollItem(GetChildren[Index]); end; function TPBTScrollItems.GetCount: integer; begin Result := GetChildCount; end; function TPBTScrollItems.Add: TPBTScrollItem; begin Result := Add(TPBTScrollItem); end; function TPBTScrollItems.Add(aScrollItemClass: TPBTScrollItemClass): TPBTScrollItem; begin Result := aScrollItemClass.Create(Self); end; procedure TPBTScrollItems.Clear; begin // can't get rid of from [0]. Not sure why. while Count > 0 do GetChildren[Count - 1].Free; Height := 0; end; procedure TPBTScrollItems.FinalizeObject; begin Clear; inherited; end; procedure TPBTScrollItems.Delete(aIndex: integer); begin if aIndex < GetChildCount then GetChildren[aIndex].Free; end; // TPBTInfoScroll function TPBTInfoScroll.GetItems: TPBTScrollItems; begin Result := TPBTScrollItems(Content); end; function TPBTInfoScroll.GetScrollIsAtBottom: boolean; begin Result := (Content.Height <= ClientHeight) or (ScrollController.ContentTop <= -(Content.Height - (ClientHeight + 10))) end; procedure TPBTInfoScroll.ScrollControllerScrolling(Sender: TObject); begin UpdateVisibleItems; end; procedure TPBTInfoScroll.InitializeObject; begin inherited; SetBarSize(CNT_SCROLLBAR_SIZE); ScrollBars := sbScrollBar; ScrollController.OnScrolling := ScrollControllerScrolling; end; procedure TPBTInfoScroll.Resize; begin inherited; Content.Width := ClientWidth - CNT_SCROLLBAR_SIZE; Refresh; end; function TPBTInfoScroll.GetScrollContentClass: TW3ScrollContentClass; begin Result := TPBTScrollItems; end; function TPBTInfoScroll.CreationFlags: TW3CreationFlags; begin inherited; // Allow key-capture and selection include(result, cfKeyCapture); include(result, cfAllowSelection); end; procedure TPBTInfoScroll.Clear; begin FDataItems.Clear; Refresh; end; procedure TPBTInfoScroll.Refresh; begin if Width > 0 then begin var wasAtBottom := GetScrollIsAtBottom; RealignItems; ScrollController.Refresh; if wasAtBottom then ScrollToBottom; UpdateVisibleItems; end; end; procedure TPBTInfoScroll.ScrollToBottom; begin ScrollController.ScrollTo(0, -(Content.Height - ClientHeight)); end; procedure TPBTInfoScroll.RealignItems; begin // recalculate the size of each itemData if (FDataItems.Length > 0) or (FNewItems.Length > 0) then Items.BeginUpdate; try var aItem := Items.Add; try var aTop: integer := 0; for var aItemData in FDataItems do begin aItem.ItemData := aItemData; aItemData.ItemHeight := aItem.Height; aItemData.ItemTop := aTop; Inc(aTop, aItemData.ItemHeight); end; for var aItemData in FNewItems do begin FDataItems.Add(aItemData); aItem.ItemData := aItemData; aItemData.ItemHeight := aItem.Height; aItemData.ItemTop := aTop; Inc(aTop, aItemData.ItemHeight); end; FNewItems.Clear; Content.Height := aTop; if Content.Height < ClientHeight then Content.Height := ClientHeight; finally aItem.Free; end; finally Items.EndUpdate; end; end; procedure TPBTInfoScroll.UpdateVisibleItems; var aStartIdx, aEndIdx, aDataIdx, viewTop: integer; begin viewTop := ScrollController.ContentTop; if viewTop < 0 then viewTop := -viewTop; // find first visible itemData // start from the bottom of the list aStartIdx := FDataItems.Length; while (aStartIdx > 0) do begin Dec(aStartIdx); if FDataItems[aStartIdx].ItemTop <= viewTop then break; end; // find last visible itemData aEndIdx := aStartIdx; while (aEndIdx < FDataItems.Length) do begin if (aEndIdx = FDataItems.Length - 1) or (FDataItems[aEndIdx].ItemTop > viewTop + ClientHeight) then break; Inc(aEndIdx); end; Items.BeginUpdate; try if (aStartIdx >= FDataItems.Length) then Items.Clear else begin // adjust item count while Items.Count < (aEndIdx - aStartIdx) + 1 do Items.Add; while Items.Count > (aEndIdx - aStartIdx) + 1 do Items.Delete(Items.Count - 1); aDataIdx := aStartIdx; for var ii := 0 to Items.Count -1 do begin var aItem := Items[ii]; aItem.ItemData := FDataItems[aDataIdx]; Inc(aDataIdx); end; end; finally Items.EndUpdate; end; end; procedure TPBTInfoScroll.AddItem(aCallback: TPBTInfoScrollAddItemDataCallback); const margin = 5; begin var aItemData: TPBTScrollItemData := aCallback; if Width = 0 then begin FNewItems.Add(aItemData); exit; end; var wasAtBottom := (ScrollController.ContentTop <= -(Content.Height - (ClientHeight + 2 * margin))); Items.BeginUpdate; try if FDataItems.Length > 0 then aItemData.ItemTop := FDataItems[FDataItems.Length - 1].ItemTop + FDataItems[FDataItems.Length - 1].ItemHeight; FDataItems.Add(aItemData); var aItem := Items.Add; aItem.ItemData := aItemData; aItemData.ItemHeight := aItem.Height; Content.BeginUpdate; try Content.Height := aItem.Top + aItem.Height; if Content.Height <= ClientHeight then begin Content.Height := ClientHeight; wasAtBottom := True; end; finally Content.EndUpdate; end; finally Items.EndUpdate; end; ScrollController.Refresh; if wasAtBottom then ScrollToBottom; UpdateVisibleItems; end; procedure TPBTInfoScroll.AddText(const aText: string); begin AddItem( lambda Result := new TPBTScrollItemData; Result.ItemText := aText; end ); end; procedure TPBTInfoScroll.AddChat(aChatType: TPBTChatType; const aChatHeader, aChatText: string; aChatColor: TColor); begin AddItem( lambda Result := new TPBTScrollChatItemData; TPBTScrollChatItemData(Result).ChatType := aChatType; TPBTScrollChatItemData(Result).ChatHeader := aChatHeader; TPBTScrollChatItemData(Result).ChatText := aChatText; TPBTScrollChatItemData(Result).ChatColor := aChatColor; end ); end; procedure TPBTInfoScroll.AddInfo(const aInfoText: string); begin AddItem( lambda Result := new TPBTScrollInfoItemData; Result.ItemText := aInfoText; end ); end; procedure TPBTInfoScroll.AddError(const aErrorText: string); begin AddItem( lambda Result := new TPBTScrollErrorItemData; Result.ItemText := aErrorText; end ); end; function TPBTInfoScroll.MaxInfoWidth: integer; var aMax: string; begin while aMax.Length < 70 do aMax += 'XXXXXXXXXX'; Result := MeasureText(aMax).tmWidth + GetBarSize; end; function TPBTInfoScroll.MinInfoWidth: integer; begin Result := MaxInfoWidth div 2; end; end. There are some bits that specific to my Paintball Net project, like the style names, which I have keyed to CSS. -David
  16. 2 points
    lynkfs

    Calendar Control

    Easiest way : procedure TForm1.InitializeForm; begin inherited; // this is a good place to initialize components var CalenderBox : TW3Panel := TW3Panel.Create(self); CalenderBox.SetBounds(10,10,252,266); CalenderBox.handle.id := 'CalenderBox'; var myCalendar : variant := new JObject; asm @myCalendar = new dhtmlXCalendarObject("CalenderBox"); end; myCalendar.disableDays("week", [7]); myCalendar.setDateFormat("%Y-%m-%d"); myCalendar.setHolidays("2019-03-19"); var yesterday : float := Now()-1; myCalendar.setInsensitiveRange(null, DateToStr(yesterday)); myCalendar.attachEvent("onClick", procedure(date:variant) begin writeln(myCalendar.getFormatedDate('%d-%m-%Y')); end); myCalendar.show(); end; make sure you include the necessary dhtmlx files in your index.html (Project Manager pane, right-click, add Template and select default.html from the Templates directory. This creates a new unit Custom Template. Then add the 2 lines below) . I used the cdn version (which contains the whole suite) <link rel="stylesheet" href="http://cdn.dhtmlx.com/edge/dhtmlx.css" type="text/css"> <script src="http://cdn.dhtmlx.com/edge/dhtmlx.js" type="text/javascript"></script> (alternatively you can import these files in code too)
  17. 2 points
    lynkfs

    node ground zero

    I had the idea firmly rooted in my mind that Node is for server-side deployment only. This happens to be not the case, it is apparently feasible to use Node packages in the browser ! Talking about packages, the largest store of Node packages is NPM, which stores some 500,000 packages or more. (350,000 as per jan 2017) Not all of those are suitable for use in the browser, but a lot of them are. The ones which don't work client-side are the ones that require internet access, graphics or files, so this is not a work-around to be able to access databases on the client without going through a server, or enabling to write files client-side. Probably a good thing. One of the ways to use an existing node package (or a self-written one) client-side is Browserify. This product basically takes a packages and iterates through all its dependencies to other packages and collates them in a single bundle. Which can then be used client-side. Browserify also has written client-side versions of some of Node's core modules, which are automatically included in a bundle when necessary. HTTP and HTTPS for instance. That still doesn't make it possible to set up a http server within the browser, but other http-functions will work : the first program in this thread (see top of this thread) was a vanilla js node program which accesses www.random.org (a site which generates random integers) and prints out the value to the console. in plain vanilla javascript : var https = require('https'); //The url is: 'https://www.random.org/integers/?num=1&min=1&max=10&col=1&base=10&format=plain&rnd=new' var options = { host: 'www.random.org', path: '/integers/?num=1&min=1&max=10&col=1&base=10&format=plain&rnd=new' }; callback = function(response) { var str = ''; //another chunk of data has been received, so append it to `str` response.on('data', function (chunk) { str += chunk; }); //the whole response has been received, so we just print it out here response.on('end', function () { console.log(str); }); } https.request(options, callback).end(); Browserifying this package and accessing it from the browser works fine, and can be an alternative to issuing xmlhttprequests Another example using Node's event mechanism // require the core node events module var EventEmitter = require('events').EventEmitter //create a new event emitter var emitter = new EventEmitter // set up a listener for the event emitter.on('test', function (message) { console.log(message) }) // emit an event emitter.emit('test', 'this is an event test') works fine as well, an alternative to add event listeners etc It wouldn't be too difficult to Smarten this up either. I'm having a look at the other 500,000 packages. Daunting but interesting.
  18. 1 point
    jarto

    restrict width of page?

    Yes, because left is changed to 0 when the form is shown.
  19. 1 point
    warleyalex

    Beware of box shadows!

    I think box-shadow CSS will need 3 things to work on iOS: 1) Vendor Prefix on the box-shadow -webkit-box-shadow: {...} 2) Border Radius set, example to 1px border-radius: 1px 3) disabled appearance -webkit-appearance: none; //--> remember to put it before box-shadow, not after
  20. 1 point
    it works fine you have to add these lines to your index.html file <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script> <script src="json-rpc-client.js"></script> and get this json-rpc-client.js file physically in the same directory as your index file code I used : procedure TForm1.W3Button2Click(Sender: TObject); begin var client : variant := new JObject; asm @client = new JSONRpcClient({ 'url': 'https://user-api.simplybook.me', 'headers': { 'X-Company-Login': 'lynkfs', 'X-Token': '89d85d820355.....................57eae4a72579bb' }, 'onerror': function (error) {} }); end; var services : variant := client.getEventList(); writeln(JSON.stringify(services)); //{"1":{"id":"1","name":"LynkFS","duration":"60","buffertime_before":"0","buffertime_after":"0","hide_duratio.... end; use either 'http' or 'https' for both the links to '//user-api.simplybook.me' and '//ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js' , depending on what your server is set up for
  21. 1 point
    jarto

    Chat Scroller - Need Some Tips

    I'm going to take a break from my Chromium work and have a look if I can modify ListBox to work with variable row heights. Edit: That's not too difficult to do. It'll just slow down a bit the calculation of rows to be shown. With variable heights you need to loop through all items when with fixed heights you can calculate which ones should be shown. However, having dynamic row heights is a pain and I believe that a chat control would need those. In that case the height of all items change during a device flip or window resize. And I believe that a proper chat control actually needs dynamic size. So, how would I do this? I'd set the min-width of the items to 100% and use PositionMode pmRelative. Then you should be able to just add and remove controls while the browser takes care of resizing and stacking them. Something like this: procedure TForm1.W3Button1Click(Sender: TObject); // Add to the end begin var Item:=TW3DivHtmlElement.Create(Content); //Or anything else Item.PositionMode:=pmRelative; w3_setStyle(Item.Handle, 'min-width', '100%'); Item.InnerText:='Hello world! I am control number '+IntToStr(Content.ControlCount)+'. How are you doing there?'; end; procedure TForm1.W3Button2Click(Sender: TObject); // Remove 1st. begin Content.ForEach( function(const Child: TW3TagContainer): TEnumResult begin Child.Free; result:=erBreak; end); end; procedure TForm1.InitializeForm; begin inherited; // this is a good place to initialize components Content.NativeScrolling:=True; end;
  22. 1 point
    Beezooka

    My SMS Project: Paintball Net Revival

    I played all the clones through the years infrequently and paintball-net will always be my favorite. Hvpb took it in a interesting route but the no class system is the best route imo. Excited to get back to splatting and maybe play some tournaments down the road! Thanks for bringing it back online and glad you had fun compiling it DavidRM! formerly KevDog. Currently Beezooka
  23. 1 point
    Ok, this first follow up is what I did to get it working based on some of the links for handling forms in javascript. Fortunately because I could mix and match SMS Pascal and Javascript, I get the best of both worlds. On my Smart Form, I have a TW3Button and a TW3EditBox with a type of ltFile. Then with the following javascript, I can get the form posted correctly to a backend which is able to capture the file and other variables of the form. Note that the other elements are created dynamically based on an example I was using. The ShowMessage and Alert items were just feedback mechanisms for me to see this part working. Both my DMVC server and a PHP script were able to process this. I am sure that there is a more friendly SMS way of writing this, but for now it got my form working and I was able to continue to plow forward with this upload piece being reasonably well contained. My key take way was getting the file upload field and the code below did the trick. This may not be the best way, but at least it was a starting point. myFile = document.getElementById('id', 'fupload1'); The InitializeObject was important to label the TW3EditBox element correctly. Below that is the code that I stuck in the OnClick event for the button. I saw many other ways to deal with this, but I just wanted to keep it straight forward for the UI. procedure TForm_ImportFile.InitializeObject; begin inherited; {$I 'Form_ImportFile:impl'} W3ButtonBack.InnerHTML := '<i class="fa fa-arrow-left fa-2x"></i>'; // this is a good place to initialize components W3EditBox1.handle.setAttribute('name','fupload'); W3EditBox1.handle.setAttribute('id', 'fupload1'); // Initialize EditBox id to find it with getElementById below W3EditBox1.InputType := itFile; end; // BUTTON CLICK EVENT CODE ShowMessage('Starting'); asm var formData = new FormData(); alert('Trying to get file box'); myFile = document.getElementById("fupload1").files[0]; alert('Got file: ' + myFile.name); formData.append("username", "Groucho"); formData.append("accountnum", 123456); // number 123456 is immediately converted to a string "123456" // HTML file input, chosen by user //formData.append("fupload", fileInputElement.files[0]); //var myFile = new Blob([content], {type: "text/plain"}); formData.append("userfile", myFile, myFile.name); // JavaScript file-like object var content = '<a id="a"><b id="b">hey!</b></a>'; // the body of the new file... var blob = new Blob([content], { type: "text/xml"}); formData.append("webmasterfile", blob); var request = new XMLHttpRequest(); request.open("POST", "http://127.0.0.1:2001/file"); request.send(formData); end; ShowMessage('Done');
  24. 1 point
    Daniel Eiszele

    RTL Class Browser stays empty

    I added the commandline arguements into a batch file which works for me!
  25. 1 point
    petermm

    RTL Class Browser stays empty

    Finally it works. Must have to do something with the ShortCut. Starting SmartUpdate via commandline is OK.
×