Importing gambas components into your projects (guide)

Ask about the individual Gambas components here.
Post Reply
User avatar
BruceSteers
Posts: 1521
Joined: Thursday 23rd July 2020 5:20pm
Location: Isle of Wight
Contact:

Importing gambas components into your projects (guide)

Post by BruceSteers »

Importing Gambas components/classes
A rough guide/example

We've all been in the situation where we are using a gambas component but it does not quite do what you want it to.

One option in this case is to get on the gambas developers mailing list and ask if they could add the feature. and who knows it might be a good idea and they may add it. but then it only exists in the new development version of gambas and no other.

But it also might not be possible, maybe the component you want to change is used by gambas itself and the developers do not wish to modify it. Maybe they just do not want the change as they do not see it to be worthwhile.

Do not fear though, for MANY gambas components and functions there is another way for us.
Quite a number of gambas components are written in gambas and made in such a way that it may be fairly easy to import the function or component into your own project and then you can modify the code to your hearts content.

There are a variety of advantages to doing this.
  • Modify / Append / Upgrade / Trim / Whatever you want the the component.
  • All modifications to the imported component are always with your program on any gambas version.
  • Newer components with upgrades and fixes will work on older gambas (may require a little code adjustment for backward compatibilty)
There are a variety of Disadvantages to doing this.
  • New upgrades fixes on the original gambas component will not happen to yours, you will have to upgrade manually.
  • Changes in gambas could affect your app.
    For example modifying gambas settings files with a modified gb.settings,
    You could import the gb.settings component and use it to save/load setting in your own app without any issue, but if you import gb.setting to modify settings files saved by gambas itself then that format may change at some point making your app not work on them.

I will give a quick guide here on how to import 2 types of things.
Firstly a whole component, I will import the gb.settings component code then remove the default one via the project settings.
Secondly a class from a component, I will extract the Message.class from gb.gui.base but keep gb.gui.base in our project yet have a custom Message() command.

First.
A whole component

The gb.settings component.

The other day on the gambas dev mailing list someone wanted the Settings.class to be able to handle comment lines they had in a VB ini file.
The Settings.class allows for a single comment line on the first line of the settings file by adding an argument when creating a new settings class.

  Dim mySettings As Settings
  mySettings = New Settings(Settings.Path, "This is my 1st line title")

Benoit was VERY clear about the Settings.class though that it is used internally by gambas and he is not willing to make any solid changes to it to make it work on ini files, and also how it saves data is subject to change at any time. it Currently saves a file a lot like an ini file but it is just a coincidence. this means that if you have made an app that uses Settings.class to process ini files then at sometime it could stop working as the gambas Settings files format could change, Another good reason to import the class into your program as then it will not change.

So.. How to import the component / classes.
  • Step 1: Get the latest gambas source code from https://gitlab.com/gambas/gambas.
  • Step 2: In your projects .src/ folder Create a folder called "Settings" (the folder name can be anything)
  • Step 3: navigate to the gambas source folder comp/src/gb.settings/.src and copy 2 files Settings.class and _Settings_Keys.class into the Newly created MyProject/.src/Settings/ folder (the main.module file is a test file we don't need).
  • Step 4: If your project is already using the gambas gb.settings component then remove it from the project Properties page components list.
And that's it. Now you are not using Gambas built in gb.settings component but have your own version with your project that you are free to manipulate and edit as you please to suit your needs.

Here is a project with the edited Settings class I did for the above mentioned conversation,..
AltSettings.zip
It works the same as you would normally use Settings but has a few extra features..
  • The top line comment is not only settable at creation with the above mentioned method now, you can use Settings.Title = "my comment" at anytime to set the comment line or CurrentComment = Settings.Title to get it.
  • SetSlotTitle(Slot As String, Title As String) , A new command that will set a comment below the slot line of any existing [Slot]
  • GetSlotComment(Slot As String) As String , this will read the comment line of a named slot.
  • SetTitleIfChanges(Title As String) , This sets the main top comment but does not trigger the settings to save. it is only saved if any other settings change and get saved.
If those are not the modifications you require then follow the above process and make the changes you need.

As simple as that for a complete component import.




Next.
Now Importing just a function/class

The Message() function from gb.gui.base

Right here's my scenario .
I want in my app the Message() function to support more then 3 buttons for easier multiple choice questioning and i also want more images to choose from. Sigh, I better ask the gambas team if they can ma.... wait , hang on......

i could DIY this..

The Message() function unlike the gb.settings component is a bit more tricky because it's a small function/class inside a much larger component that i want to keep gb.gui.base.
So what we will have to do is import Just the Message.class and it's related files but we will have to rename it otherwise it will conflict with gb.gui.base Message()

For this implication I will rename it to Choice() as it's reason is more choices.

So.. How to import the function / classes.
  • Step 1: Get the latest gambas source code from https://gitlab.com/gambas/gambas.
  • Step 2: In my projects .src/ folder i Create a folder called "Message" (the folder name can be anything)
  • Step 3: Message is a part of the gb.gui.base component so navigate to the source folder comp/src/gb.gui.base/.src/Message and copy all 3 files into my Newly created MyProject/.src/Message/ folder
  • Step 4: Rename the Message.class file Choice.class
  • Step 5: Copy the Message folder from the gb.gui.base/ folder into your main project folder. these have the images that display.
  • Step 6: Replace the text in the 2 lines in FMessage.class that refer to the folder "./gb.gui.base/message" with "./message"
Now you have your own version of the Message() command called Choice()
At present it works identical to Message with things like Choice.warning() Choice.Error()

Here is a version I modified with a test app...
AltMessage.zip
It works the same as you would normally use Message but has a few extra features..
  • ALL types of Message can have as many buttons as you want.
  • Buttons are passed as a string array Ie. iValue = Choice("Choose a Number", ["1", "2", "3", "4, "5"])
  • New property AutoResize with ALL commands, sets auto-resize on all the buttons so they all fit their text, without it ALL buttons are the size of the largest text.
  • New property SeperateCancel with ALL command puts a Spring between the cancel button and the rest (cancel is always the right hand button)
  • New command Choice.Custom(sMessage As String, Optional Buttons As String[], Optional Type As String = "question", Optional SeparateCancel As Boolean, Optional (AutoResize) As Boolean) As Integer
  • The "Type" argument of Choice.Custom gets the stock image by name. ANY stock image name can be used. default to 'info' if image is not found.
  • The test app included with the project lets you edit the settings to test the look.
Example Usage...
iVal = Choice.Question("Select a number", ["1", "2", "3", "4, "5", "Cancel"], True, True) ' buttons match text size and there is a space between cancel button and the others

iVal = Choice.Error("Error, How many times do you want to panic?", ["1", "2", "3", "4, "5", "Cancel"]) ' buttons evenly spaces and all the same size as the Cancel button

iVal = Choice.Custom("Select a help page", ["1", "2", "3", "4, "5", "Cancel"], "help",,True) ' buttons evenly spaces and all fit their text and the help stock icon is used for the image.
AltMessage.png
Again this may not be what you require so get the generic Message and make your own modifications you do need :)

I have just shown the way. ;)

There are no documents for the attached files, read the code and work it out ;)

Hope that may help some of you :)
Wishing well
Bruce
Last edited by BruceSteers on Monday 1st March 2021 6:23am, edited 4 times in total.
If at first you don't succeed , try doing something differently.
BruceS
User avatar
BruceSteers
Posts: 1521
Joined: Thursday 23rd July 2020 5:20pm
Location: Isle of Wight
Contact:

Re: Importing gambas components into your projects (guide)

Post by BruceSteers »

Additional...

Another method for creating your own custom objects is with Inheritance.

You can make a new class file and use the Inherits keyword to make it inherit all the properties of another object.

Consider the following class file.....
The file name is myGrid.class
After adding this file to my .src/ directory and compiling i will get a different gridview object available in the IDE called myGrid
It copies all the properties from the GridView object but has 4 additional features.

myGrid1.SetColumns(string[]) ; sets the column count and text for each header
eg. myGrid1.SetColumns(["column 1", "column 2", "column 3"]) sets Column.count to 3 and fills the header texts

myGrid1.MoveRowUp(optional row as integer) ; moves the currently selected or supplied row number up 1
myGrid1.MoveRowDown(optional row as integer) ; moves the currently selected or supplied row number down 1

Plus the Sort() event is automatically enabled and sorts using the columns set in Columns.Sort.

Things to note...
Setting the _Similar Const to "GridView" allows the IDE to change an existing GridView into a myGrid using the "Change into" option

Setting _DrawWith Const to "GridView" tells the IDE how to show the control (it displays it just like a gridview)

I was able to intercept the _RaiseSort() event and utilize it myself but some objects are not so easy to grab the events.
you will have to play and discover what can be used.

All the best
Bruce

' Gambas class file

Export
Create Static
Inherits GridView

Public Const _Similar As String = "GridView"
Public Const _DrawWith As String = "GridView"

'' Move currently selected row (or supplied Row number) up 1
Public Sub MoveRowUp(Optional (Row) As Integer = -1) As Boolean

  If (Not Me.Rows.Count) Or If (Row = 0) Then Return
  If (Me.Row <= 0 And Row = -1) Then Return

  Dim iPos As Integer = IIf(Row = -1, Me.Row, Com.Bounds(Row, 0, Me.Rows.Max))
  Dim sData As String
  Object.Lock(Me)
  For c As Integer = 0 To Me.Columns.Max

    sData = Me[iPos, c].Text
    Me[iPos, c].Text = Me[iPos - 1, c].Text
    Me[iPos - 1, c].Text = sData
    Me.Rows[iPos - 1].Selected = True

  Next

 Object.UnLock(Me)
 Raise Select
 Return True
 End



'' Move currently selected row (or supplied Row number) down 1
Public Sub MoveRowDown(Optional (Row) As Integer = -1) As Boolean

  If (Not Me.Rows.Count) Or If (Row = Me.Rows.Count - 1) Then Return
  If (Me.Row = Me.Rows.Count - 1 And Row = -1) Then Return

  Dim iPos As Integer = IIf(Row = -1, Me.Row, Row)
  Dim sData As String
  Object.Lock(Me)
  For c As Integer = 0 To Me.Columns.Max

    sData = Me[iPos, c].Text
    Me[iPos, c].Text = Me[iPos + 1, c].Text
    Me[iPos + 1, c].Text = sData
    Me.Rows[iPos + 1].Selected = True
  Next

 Object.UnLock(Me)
 Raise Select
 Return True
 End

'' Sets column count and header texts from the given array.
'' Eg. SetColumns(["Column 1", "Column 2", "Column 3"])
Public Sub SetColumns(sTexts As String[])
  
  Me.Columns.Count = sTexts.Count

  For c As Integer = 0 To sTexts.Max
    Me.Columns[c].Text = sTexts[c]
  Next
  
End


Public Sub _RaiseSort()

  If Me.Rows.Count = 0 Then Return

  Dim Values, ValueSorted As New String[], SelectList As New Integer[]
  Dim Nx, iNx As Integer, sel As String

  If Me.Row >= 0 Then sel = Me[Me.Row, Me.Columns.Sort].Text

  For Nx = 0 To Me.Rows.Max
     Values.Add(Me[Nx, Me.Columns.Sort].Text)
     SelectList.Add(Me.Rows[Nx].Selected)
  Next

  ValueSorted = Values.Copy()
  ValueSorted.Sort(IIf(Me.Columns.Ascending, gb.Ascent Or gb.IgnoreCase, gb.Descent Or gb.IgnoreCase))

  For Nx = 0 To ValueSorted.Max
    For iNx = 0 To Me.Columns.Max
      Swap Me[Nx, iNx].Text, Me[Values.Find(ValueSorted[Nx], 0, Nx), iNx].Text
    Next

    Values.Clear()
    For iNx = 0 To Me.Rows.Max
        Values.Add(Me[iNx, Me.Columns.Sort].Text)
    Next
  Next

  If sel <> "" Then
    For iNx = 0 To Me.Rows.Max
      If sel = Me[iNx, Me.Columns.Sort].Text Then 
        Me.Rows[iNx].Selected = True
        Break
      Endif
    Next
  Endif

  Me.Refresh()


  Raise Sort ' raise the sort event in the parent class in case you want to detect it there.

End

If at first you don't succeed , try doing something differently.
BruceS
Post Reply