It used to be that as an existing user of REAL basic or REAL studio there was a discount when you updated. So instead of paying full price yearly you’d pay a reduced rate to update.
With that model new users, especially ones who bought the more expensive licenses, made sense to court since the revenue from them could potentially be higher. We’ll ignore that its costs more to sell to a brand new user.
But that changed some years ago and now there is no discount for an existing user to renew or a new user to buy a license.
And this is why I say Xojo should switch its focus from courting new users to existing users.
A new user has no investment in existing code. So its easy for a new user to look the tool over and just walk away without buying. An existing user already HAS some code base, large or small, and has a reason to want to update.
Existing users _should_, according to all the marketing people I know and have ever talked with, be easier to sell an update to than trying to sell to a new user. So it should be commensurately less costly to sell to existing users.
Since there’s no discount for existing license upgrades vs new licenses I really do not understand why Xojo wouldn’t focus on existing users and their needs instead of new users.
They stand to make at least the same amount of money if they can convince the same number of existing users to update vs selling to new users they have to “sell”. And maybe thats the rub ? Do they have a difficult time convincing new users to renew ?
I know that so far the people I have spoken to have said they have no compelling reason to update. OR “I would but I dont need all that API 2 stuff” – although “stuff” isnt the word usually used.
And this gets back to my original point. Xojo is pursuing things that do not focus on existing users needs and is doing so at their own financial peril. So they spend more money than they need to to court new users because existing users arent updating ?
Maybe the thing to do is ask existing users WHY they arent updating and tackle those problems rather than trying to just court more new users – who next year arent new users and maybe dont update so you need to court more new users … and so on and so on.
There are lots of things that strike me as curious.
One of them is that that despite so many people needing third party code, plugins, or other add ons that Xojo doesn’t seem to promote or cultivate those folks who help them make their product better.
MS did this with VB from the outset. I dont know if the VB 1 team realied that they would never be able to supply 100% of the things that people wanted or just loved the idea of new custom controls in dll’s but they built that in early and its arguably one of the things that made VB so successful. (TIOBE lists VB at #6 still even after the release of VB.Net and several other efforts at moving VB out of the mainstream)
That said having a feature, like custom controls, isn’t the same as actually promoting it OR making it possible for third parties to access resources specific to their needs for creating custom add ons. MS was always very good at that and VB’s success speaks to that.
On the other hand while Xojo does have plugins there are only a very tiny handful of developers that anyone even knows about. MBS and Einhugur. There are a few other but the IDE doesn’t ship with samples or demo versions of any of them, no sample project or demos of them, and so a lot of people have no idea they exist.
Theres a really nice report writer that, once people realize they one, always gets recommended. So why isn’t it in the IDE as a default add on that maybe watermarks whatever you use it for until such time as you pay for a license ?
The IDE itself could have a lot of these add ons available for people to quickly and easily use – and that would go a long way towards promoting the third party market AND raising awareness that there even ARE such things available.
Beyond this there’ no single resource at Xojo that seems to be the point person for plugin authors to converse with. Nor anyone whose role is to find and secure new components for Xojo to include in its standard toolbox or as add ones they could ship demo version of.
Growing the third party markets isn’t just something that is good for third parties. Its good for Xojo as well because people looking at the tool see there are lots of things they can use with the tool to accomplish an ever widening range of tasks.
While having a web, ios, android, desktop and console targets widens the range of targes people can try to write their software for if the only things that can be created are whats in the box already OR “write your own” thats a decent impediment to actually deciding to write your software for other targets.
If, on the other hand, you can see that you can get a large set of add ons, custom controls and other things that will help you accomplish your task then that choice becomes a lot easier.
Come on Xojo. you can dedicate some time and effort to this and become VB for the 2020’s.
There are two “text handling” types in Xojo – String and Text.
And they vary quite a bit in how they handle textual data.
While strings use UTF-8 as their default encoding you still have to worry about what form of UTF-8 the characters in the string are in. Strings dont deal with “characters” in the way you and I perceive them.
For instance if you run this code
Dim s1 As String = "ΓΌ"
Dim s2 As String = &u75 + &u308
Break
What you see in the debugger as the text they hold is the same
But if you chnage this to
Dim s1 As String = "ΓΌ"
Dim s2 As String = &u75 + &u308
If s1 = s2 Then
Break
End If
what you will find is that while they appear to you and me to hold the same contents they are not “the same”. And this is because the first one uses one form of UTF-8 (composed characters) and the second uses a different form (decomposed characters)
And theres no built in mechanism to know one is in one form or the other nor any to convert one into the other π
So years ago the “next” framework was created and a new type added – Text. And it deals with these issues much better. That same code, using text, looks like
Dim t1 As Text = "ΓΌ"
Dim t2 As Text = &u75 + &u308
If t1 = t2 Then
Break
End If
but this time when you run you WILL hit the break point. Text handles the different forms seamlessly and you get the result you expect.
And the differences go further than this. When you split a string up into “characters” you get different numbers of characters from the two apparently equal strings. Not so with text.
Dim s1 As String = "ΓΌ"
Dim s2 As String = &u75 + &u308
If s1 = s2 Then
Break // wont stop here but you might expect it should
End If
Dim s1Chars() As String = s1.Split("")
Dim s2Chars() As String = s2.Split("")
Break // note that s1chars. ubound < s2chars.ubound
// and the contents are totally different
Dim s1CodePoints() As UInt32
For i As Integer = 1 To s1.LenB
s1CodePoints.Append AscB(s1.MidB(i,1))
Next
Dim s2CodePoints() As UInt32
For i As Integer = 1 To s2.LenB
s2CodePoints.Append AscB(s2.MidB(i,1))
Next
Break // again the ubounds are different - this time they should be !
Dim t1 As Text = "ΓΌ"
Dim t2 As Text = &u75 + &u308
If t1 = t2 Then
Break
End If
Dim t1Chars() As Text = t1.Split
Dim t2Chars() As Text = t2.Split
Break // note that t1Chars.ubound = t2Chars.ubound
// and the chars are "the same" !!!!!!
Dim t1CodePoints() As UInt32
For Each cp As UInt32 In t1.Codepoints
t1CodePoints.Append cp
Next
Dim t2CodePoints() As UInt32
For Each cp As UInt32 In t2.Codepoints
t2CodePoints.Append cp
Next
Break // these should differ since one uses one form of utf-8
// and one uses a different one
Text just handles things seamlessly
With the transition to API 2 it will be a shame if String doesnt adopt some of these capabilities AND there’s no framework provided means to normalize string so they all use UTF-8 composed or decomposed so we can deal with the inconsistencies that can arise.
Recently there was a blog post on Xojo’s blog from Wayne Golding about how 2019r2 and newer accept VAR as a keyword to declare a variable. And they still accept the older DIM.
There are some issues with VAR IF you ever have to move your code to an older version. I know a lot of developers of commercial software that have to do this so they can continue to support their clients running older versions of various operating systems.
These people have stayed with the older DIM so they can easily move their code simply.
Adding VAR as a keyword has some issues though. While I agree that VAR is an awful parameter or variable name there is code that has used it that now suddenly won’t compile.
One thing I asked for was rather than adding a new keyword, with all the issues that can bring, that we just NOT have to put a DIM or VAR. Then we could write
dim foo as integer
var bar as double
d as date
In a compiler there is usually a “grammar” – a set of rules for what the compiler considers valid code and what it doesn’t. And somewhere in Xojo’s grammar there is a rule that says a variable declaration has to look a specific way. (This is a wild assed guess on my part not actual code since I do not have the code for this)
variableDecl :
DIM listofVariablesAndTypes
| VAR listofVariablesAndTypes
and listofVariablesAndTypes is additional rules for how to declare one or more variables with default values in a single line. Making this grammar rule
variableDecl :
DIM listofVariablesAndTypes
| VAR listofVariablesAndTypes
| listofVariablesAndTypes
(or something like this) would make it so DIM and VAR are not required.
Well in this case more like “Huh I guess I should finish that damned thing up” π
When I watched the recent keynote there was one bit that made me think exactly that. It was the point where geoff was showing off drawing from a XojoScript.
I’d tinkered with something on and off for a while – and its been more off lately than on.
But when I saw that I thought “Gee. I really should finish demo that up.”
So I did. And here you are. A way to draw from XojoScript to a graphics that resides in Xojo code. In the demo it comes from the paint event of a canvas.
Doesn’t currently support any additional clipping of the graphics but I’ll see what I can do.
There’s no support for Pictures since pictures do not exist in XojoScript. That might also be possible to do without too much effort but I dont have any need for it at the moment.
The ONE thing to remember with XojoScript is it is all very CLASSIC API 1.0
So extension methods like String.Mid, String.len etc dont exist (I could add those too but …)
First – download and install Rust if you dont have it. You’ll need to verify that it is installed correctly.
I’ve been using cargo, the Rust package manager, to configure new projects to work on. And I have all my projects in a directory where I can access them easily.
Most of the work is in Terminal so start up a terminal window.
Change to your directory where you want the project to be saved. For me this is in ~/RustProjects
cd ~/RustProjects
Create a new project using cargo – I called mine FirstRustLib
cargo new FirstRustLib
This will create a new directory with all the bits we’ll need inside it1. Inside is a Cargo.toml file and a src directory. The TOML file is a project config file. It lets you specify all kinds of dependencies on other Rust libraries, settings and all kinds of other bits (but thats an entirely different topic)
Open the Cargo.toml file as we do need to make some changes to it. Any plain text editor (BBEdit, TextWrangler, even TextEdit will do just fine)
In the TOML file you will see that its already preconfigured with several items that are used to identify the pakage. We need to add a new section to this to tell cargo that instead of a normal Rust application we want this package to be compiled as a dylib (not a Rust library as thats only usable within Rust)
We need to add the following
[lib]
crate_type=["cdylib"]
name="our_rust"
the “crate type” entry tells Cargo what kind of output this project is going to build. In this case its a normal C style dylib. The name entry tell Cargo what to name the built dylib (it will end up being named starting with lib – in this case libour_rust.dylib)
And thats is so save these changes.
The way we created the new project Cargo has created a new file in the src directory named main.rs. For a library this should be named lib.rs – otherwise when we build using Cargo it will complain. Rename main.rs to lib.rs
If you change into the directory FirstRustLib
cd FirstRustLib
and run a build
cargo build
you will build the project as it sits now. It wont build correctly because lib.rs actually contains code that is suited to an application, not a dylib, so you will get errors like
Compiling FirstRustLib v0.1.0 (/Users/npalardy/RustProjects/FirstRustLib)
warning: function is never used: `main`
--> src/lib.rs:1:4
|
1 | fn main() {
| ^^^^
|
= note: `#[warn(dead_code)]` on by default
Finished dev [unoptimized + debuginfo] target(s) in 1.46s
but this is entirely expected
So now, on to defining our first function for our dylib, that will be exposed. We’re not going to do anything crazy just yet. Something simple like adding two integers and returning the result.
Open lib.rs and remove ALL the code it in at present. Replace it with the following :
The first line, #[no_mangle], tells the Rust compiler NOT to mangle the names so they are exported as is. You need to do this otherwise the actual exported name will be something you have to guess at in Xojo and thats not helpful.
The next line is quite terse but can be read from left to right as :
pub – this item is accessible to outside code (other Rust code even)
extern – makes this item usable by external code (in our case Xojo)
fn – function (as opposed to a sub. In Xojo this would just be a method that returns a result)
add_numbers– this is the name of the function we’re writing
(number1: i32, number2: i32) – the parameters. number1 and number 2 are int32
-> i32 – this odd little syntax is equivalent to Xojo “as Int32” – the return type
And the entire function is surrounded by an opening { and closing }
I’m sure some folks eyes have glazed over by now – the seemingly C style syntax does that for some π
And the rest of the code in the function prints a message “Hello World” and then adds the two numbers together. Not there is not explicit “return” of the value like in Xojo.
And that simple function is all the Rust code we need. Save it and build
cd ~/RustProjects/FirstRustLib/
cargo build
If you have no syntax errors you should see something like
Finished dev [unoptimized + debuginfo] target(s) in 0.04s
And there will be new directories that have been created. Now you should have a target directory. Inside this will be a debug directory (we did not tell cargo to build a release build so it builds a debug one by default)
Inside debug are several other dirs as well as our built dylib
On to the Xojo code to use this dylib we created
Start a new Xojo desktop project. I’m using 2019r1.1 so this should work for older versions and newer versions as well.
Add a copy file step to the macOS target. Drag in the dylib in to the copy file step and set it to copy the dylib into the Frameworks directory on macOS.
To use the dylib we do need to write a declare into it. But since we used really simple types and Xojo has exact matches for the Rust one we can get away with a simple declare.
Since we copied the dylib into the FRameworks directory in the macOS bunmdle we will need to tell Xojo to locate th dylib based on the executables location like
Soft Declare Function add_numbers Lib "@executable_path/../Frameworks/libour_rust.dylib" (number1 As Int32, number2 As Int32) As Int32
@executable_path is special for macOS and literally means to use the current executables_path and then a relative path from there.
For Windows you may want to configure the build step to copy the dll to the Contents folder and then the declare would be
Soft Declare Function add_numbers Lib "libour_rust.dll" (number1 As Int32, number2 As Int32) As Int32
Then the code to use the dylib/dll is really simple – just like any method from Xojo
Dim a As Int32
Dim b As Int32
Dim result As Int32
a = 123
b = 456
result = add_numbers(a,b)
Nothing fancy just a simple call into a Rust dylib/dll we made.
And of course the obligatory “ok WHY use Rust ?”
Turns out that Rust is, by design, deliberately safer to use than many languages.
It was deliberately designed to be safe, and concurrent. And it doesn’t use garbage collection.
Module PictureMimicry
Class MyPicture
Function MyGraphics() as MyGraphics
End Function
End Class
Class MyGraphics
End Class
End Module
And now, for the answer, on how to make it so we can make the compiler catch the attempts to create a new MyGraphics.
First we ABSOLUTELY put a protected constructor on MyGraphics. That will prevent EVERY attempt to create a new MyGraphics with the NEW operator. But now how does MyPicture create one to return it ?
It doesnt π
Remember we can set the SCOPE of items in a way that they can ONLY be used by code in the module. And since Xojo IS an OO language and support inheritance and polymorphism we can use that to our advantage.
In such a language every subclass of a class IS also its parent class type.
What we’re going to do is add a private subclass whose sole purpose is to allow us to create new instances. And we can return that subclass and the code outside the module will ONLY be able to use it as the parent class type because the subclass type is not know outside he module.
The end result is this set up
Module PictureMimicry
Class MyPicture
Function MyGraphics() as MyGraphics
return new MyGraphicsSubclass
End Function
End Class
Class MyGraphics
Protected Sub Constructor()
End Sub
End Class
Private Class MyGraphicsSubclass
Inherits MyGraphics
Public Sub Constructor()
End Sub
End Class
End Module
and now in the App Open event from the previous post
dim p as MyPicture
dim g as MyGraphics
p = new MyPicture
g = new MyGraphics
The compiler will complain about the last line not being permitted. Exactly as we wanted.
And this code
Dim p As MyPicture
Dim g As MyGraphics
p = New MyPicture
g = p.MyGraphics
Works exactly as we intended.
You can now freely add methods to MyGraphics and MyPicture and know that none of your own code can accidentally create a MyGraphics in a way that you did not intend to permit.
Code in a way the compiler can help you write code the way you intended it to work. Not just by convention or habit. But by design.
Code for your own safety and sanity. Get the compiler to help you when it can.
Like a module that converts to hex, oct, binary properly. By this i mean if you pass in an Int32 you get back 32 bits worth of encoded data – 4 hex digits, 5 octal digits (since each only covers 7 bits), and 32 binary digits. And if you pass in an int8 you get back 8 bits. And so on.
You dont have to zero pad things like you would with Xojo’s built in ones.
Of course my CSV Parser is there. It handles all kinds of things you find in CSVs like embedded newlines (which you can get from Excel quite easily)
A stable quick sort. What that means is if you sort by one value then sort that sorted data by a second key the original relative positioning of items is preserved. Stable sorts are very handy when you want to implement sorts that consider multiple criteria one after the other since you can simply sort by the first key, then the next then the next and the data will remain properly sorted throughout.
No. I dont meant the whole “God created the earth in 7 days” business. Sorry but this isnt going to be a discussion of monotheism and the belief systems they have. Maybe some other time when I get tired of discussing it with my uncle, the ex-Catholic priest, and his wife, the ex-Catholic nun. It does make for some fun discussions though π
No, this is about the whole question of creating objects in Xojo and how, in our code, we can make it so some code can create new instances using NEW and other code cannot.
This pattern occurs frequently in Xojo.
Pictures can create and return Graphics objects but we cannot use NEW to create one. Queries of database objects can create & return RecordSets, but we cant create one using NEW.
So how can we accomplish this in our own code ? If we should not be creating new instances using NEW it would be nice to have the compiler help us enforce that rule. Its easy to make it so we can NOT use NEW by adding private or protected constructors. But then NO code can create a new instance. And thats not quite what we want. We want it so most code cannot create new instances, but a VERY limited set can.
In many cases some will just suggest that you do this “by convention”. All this means is “dont do that when you shouldn’t”. Its nothing the compiler helps enforce. You just dont write code that uses new with certain classes. Doing things “by convention” is often error prone. And the compiler doesnt help us at all in these cases.
Personally I don’t like things that the compiler could help me with that aren’t designed in a way that it CAN help me with. And this is one where we absolutely can design code that the compiler can help us with.
In some languages you can nest classes inside others. By doing this you can limit what is exposed in the classes that are embedded in the others. Xojo doesn’t have any means of doing this.1 So we need some other mechanism.
In some cases in Xojo you can use an interface to hide certain details from other classes. But if the interface is globally accessible then every other bit of code can still use that interface to manipulate the classes that implement it. And an interface can’t be used to create new instance so using one doesn’t seem like it will work very well.
What we need is a way to have a globally accessible class that effectively has no, or only private constructors, the some code can call to create new instances.
Lets mimic Picture and Graphics as our example. We want to work towards the same goal of making it so the compiler disallows
New Graphics
but allows
dim g as Graphics = Picture.Graphics
Lets start with putting all this in a module. We’ll call it PictureMimicry. And in that module we’ll add two classes – MyPicture And MyGraphics.
We’ll add a method to MyPicture, MyGraphics, that returns a MyGraphics object.2
Function MyGraphics() as MyGraphics
End Function
Make sure that the scope property of MyPicture and MyGraphics are both global so you can use their names anywhere without having to qualify them with the module name.
Now in our App.open event we could write
dim p as MyPicture
dim g as MyGraphics
p = new MyPicture
but, we could also add
g = new MyGraphics
and this is what we want the compiler to catch for us and tell us this is not allowed.
You might be asking “Why the heck did we put all this in a module then make everything global?” And, it turns out that everything being in a module is part of how we can make everything work the way we want.
When you put code, classes, etc in a module you can set a Scope property on those items. And one of the scope settings is Private. What this means is that no code outside the module can access these items, but things INSIDE the module can. So how does that help us ?
At first you might think “Oh great I’ll put a private constructor on the MyGraphics class and that will solve things”. But it doesn’t. That constructor is then PRIVATE to the MyGraphics class and nothing else call it. Not even code in the module can use it.
So how then do we create new instances of the MyGraphics class, make it so only code IN the module or in other classes in the module can create new instances, and any code outside the module can not ?
This is absolutely possible.
Maybe I should leave off here and let you all ponder this for a while and then follow up with the answer in another blog post. Replies are welcome as to how you think we do this.
I’m told by ex-compiler developers that the compiler could handle this BUT its not exposed in any way in any portion of the language or the IDE.
We could have used a computed property and only implemented the getter just as easily. Either works.