Jump to content

Leaderboard


Popular Content

Showing content with the highest reputation since 03/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) 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. 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
  5. 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
  6. 3 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
  7. 2 points
    warleyalex

    vendor prefix

    The CSS browser prefixes that you can use are: Android: -webkit- Chrome: -webkit- Firefox: -moz- Internet Explorer: -ms- iOS: -webkit- Opera: -o- Safari: -webkit- Please, fix the smartCL.System --> unitFCSSToken := 'Moz'; ---> I think should be lowecase w3_setStyle(FParticles.Handle, BrowserAPI.PrefixDef('transform'), 'scale(0.5,0.5)'); w3_setStyle(FParticles.Handle, BrowserAPI.PrefixDef('transform-origin'), 'left top');
  8. 2 points
    jarto

    TW3StringGrid is available

    Sure, I can add a Button column type too. Also OnCellChanged will be added in the next update. I've also been working on sorting. As the data is dynamically drawn, it's actually surprisingly easy to add. The trickiest is just to decide how the sorting interface should be done (properties etc.) so that I don't unnecessarily restrict future features. For example, in many Delphi grids there are quite detailed possibilities to control sorting.
  9. 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
  10. 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.
  11. 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
  12. 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
  13. 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)
  14. 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.
  15. 1 point
    warleyalex

    layout, wordwrap and label nightmare

    We simply cannot open your project at labelissue.zip, missing "Form1.sfm"! It's an old married, TW3Label does not like the TW3Layout. Anyway, I think the unfinished widget SimpleLabel (TW3SimpleLabel => SmartCL.Controls.SimpleLabel.pas) works "almost" as expected. projTeste.zip
  16. 1 point
    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
  17. 1 point
    jarto

    restrict width of page?

    Yes, because left is changed to 0 when the form is shown.
  18. 1 point
    jarto

    How to resize a modal dialog

    Got it. TW3BlockBox was using 100% as width and height. It's a nice trick but that also prevents javascript from triggering resizes.
  19. 1 point
    jorn

    Embed SMS Form Project in WordPress Page?

    I can't tell exactly what's wrong in your case, but it's perfectly doable. We have done it here, for example: https://showcases.smartmobilestudio.com/portfolio/missile-command/
  20. 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
  21. 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
  22. 1 point
    i'll have a look over the weekend
  23. 1 point
    lynkfs

    Copying Text to Clipboard

    Browsers pick up selected text in <textarea> and <p> type of html elements, not necessarily in each and every other type. Also they will not mess with existing clipboard data unless initiated by a user action (like clicking a button). Bit of a roundabout way of doing this, but this works (form with W3Label1 and W3Button1) procedure TForm1.W3Button1Click(Sender: TObject); begin var xCopy : Procedure(S:String); asm @xCopy = function copyToClipboard(text) { if (window.clipboardData && window.clipboardData.setData) { // IE specific code path to prevent textarea being shown while dialog is visible. return clipboardData.setData("Text", text); } else if (document.queryCommandSupported && document.queryCommandSupported("copy")) { var textarea = document.createElement("textarea"); textarea.textContent = text; textarea.style.position = "fixed"; // Prevent scrolling to bottom of page in MS Edge. document.body.appendChild(textarea); textarea.select(); try { return document.execCommand("copy"); // Security exception may be thrown by some browsers. } catch (ex) { console.warn("Copy to clipboard failed.", ex); return false; } finally { document.body.removeChild(textarea); } } }; end; xCopy(W3Label1.Caption); end;
  24. 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;
  25. 1 point
    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
×