I’ll admit that I really haven’t seriously touched R3 since most of its focus was on iOS and dark mode which isn’t something that matters to me.
The times I did start it I found it to still be slow like R2.1 and otherwise not many significant differences in the things I was trying.
Autocomplete is still glaringly bright in dark mode on macOS. I’m now wishing there was a way to turn autocomplete off its so hard on my eyes being in bold and bright white. It’s definitely NOT the code editors text color or any user selected color.
And the new default color set is harsh as well. Again its incredibly bright and in dark mode its too darned bright for my liking. Makes me really glad I made it so users could configure their own themes way back when 😛
But you be the judge. In the following pictures R3 is first and R1 is next. Both are set to use default colors. (To be honest the images do NOT show how bright those colors are here on all 3 monitors)
Beyond that I haven’t used R3 for anything. I’m still seeing too many odd bugs being reported to make me comfortable moving any client projects to it. So I’m still using 2019r1.1 – basically the last version I contributed to 🙂
In the mean time I have managed to assemble lists of differences for various project types. These are all the differences I can discern between 2019r3 and 2019r1.1
I’ll see about compiling lists of differences on Windows & Linux as well
Note that this is changes and may simply indicate that some reorganization has taken place. For instance the HTMLViewer appears to have had many events removed but it appears the code is simply reorganized so that event handlers, for instance, are not handled in a Xojo stub and re-raised but are raised directly by the HTML Viewer plugin.
EDIT – I can see that I’ll need to update how this data is presented to make situations like the event handling on the HTMLViewer more clear. It appears that the old HTMLViewer had event handlers and event definitions so the HTMLViewer would handle the event and re-raise it so you could handle the event. This appears to have been reconfigured in 2019r3 – so the event is no longer handled by the HTMLViewer and re-raised but just not handled at all so the base class event can still be handled. To the end developer this change is invisible but in the overall framework set up it is readily apparent which is why my 2019r3 differences list includes the removal of all these events.
No not your plate (despite it being right around American Thanksgiving)
Sometimes when you write your code its convenient to have several versions of “the same” method.
For instance, in Xojo you might look at the AddRow method on the Listbox class )in 2019r1.1) and see that it has several forms
ListBox.AddRow(ParamArray Item as String)
ListBox.AddRow(items() as String)
ListBox.AddRow()
This is a great example of “overloading” – having one method with many different signatures. Each is unique and distinct from the others and the compiler doesn’t have to do conversions that might lead to ambiguous situations when you pass several strings to the method.
The same can be said for the classic Date class. It had several constructors like
Date.Constructor()
Date.Constructor(CopyDate as Date)
Date.Constructor(Year as Integer, Month as Integer = 1, Day as Integer = 1, hour as Integer = 0, minute as Integer = 0, second as Integer = 0)
Date.Constructor(Year as Integer, Month as Integer = 1, Day as Integer = 1, hour as Integer = 0, minute as Integer = 0, second as Integer = 0, GMTOffset as double)
Note the last two are VERY similar but vary in the very last parameter.
And you can also do this in your own classes & code.
You could easily add many constructors to your classes so you can create instances using a wide variety of parameters.
As long as each overload can unambiguously be called the compiler wont have any trouble with things.
Its usually the “unambiguous” part that gets people into trouble as they defer to using default values for parameters. For example, if I have the following class along with the constructors shown you may find you have issues – but only sometimes
Class TestClass
Public Sub Constructor()
End Sub
Public Sub Constructor(foo as integer = 0)
End Sub
End Class
If later I have code like
Dim c As New TestClass(1)
This will work without issue since TestClass only has one possible constructor that matches that signature. So things compile & run fine.
However if later I try to do
Dim c As New TestClass
I will now get a compile error saying
Window1.Open, line 1 There is more than one item with this name and it’s not clear to which this refers. Dim c As New TestClass
The compiler cannot figure out whether I mean to call the constructor with no parameters, or the one that has a single parameter with a default value that I simply have left out. And so it gives up trying and you get a compile error.
Once you realize that the issue is that the compiler doesn’t know which method you meant you can address the problem. Perhaps in this case a constructor with a default value for the parameter makes no sense and so you’d remove that default value. Or you decide that you really only need the Constructor with the single parameter and default value since you can deal with it as if it were the no parameter version.
Often when I’ve encountered this problem it is a symptom of having optional or default values that, when omitted, make one method have the same signature as the other.
Careful out there and try not to confuse the compiler too much.
Operator_Convert is a very handy method to implement on a lot of classes. It not only lets you create a mechanism to make your classes convert themselves into other types but it also lets you create new instances FROM other data types.
So let’s start with the simple forms of operator_convert – the “convert TO” forms.
Suppose you have some class defined called “myClass”
Class myClass
End Class
and you want to make it easy to have this class write log messages to the DebugLog. You most certainly could write a “ToString” method as an explicit way to convert this class to a String
Class myClass
Function ToString() as string
End Function
End Class
and then in your code you’d use it like :
dim instance as new MyClass
// lots of intervening code
system.debuglog instance.ToString
And maybe this isn’t so bad. It IS explicit which is most times a good thing. Your code will have a certain clarity to it because of this style.
But there is another way to do much the same thing that is also effective – despite it being slightly hidden and magical like a variants ability to automatically convert itself to other data types by assignment. And this “magic” is operator_convert
In a class like our sample
Class myClass
Function Operator_Convert() as string
End Function
End Class
And now your usage could look like
dim instance as new MyClass
// lots of intervening code
system.debuglog instance
Since System.Debuglog takes a string as a parameter the compiler figures out that our class has a suitable Operator_convert method and will call that for us.
You can define many operator_convert functions that convert TO other datatypes and as long as they dont cause any ambiguity you will get the results you expect. You may get an ambiguous situation of you define a Convert To operator_convert for Integer, Int32 and/or Int64 since Integer is simply an alias to one of those types depending on whether you are building for 32 or 64 bit. There can be others but this is the most common.
There is another form for operator_convert – the “convert FROM” form.
In this case operator_convert behaves in many ways like a constructor called with a parameter. However one thing to remember is that Operator_convert is NOT a constructor! Its an initializer at best. If your class depends on constructors being called make sure when you use the convert FROM form you call your constructor !
For our sample class suppose we want to initialize it from a string.
Class myClass
Function Operator_Convert() as string
End Function
Sub Operator_Convert(initializeWith as string)
// if we rely on the constructor having been called
// then call the Constructor here !
// now set our class up from whatever data is in the
// parameter passed
End Sub
End Class
and we could use it like
dim instance as MyClass = "123"
// lots of intervening code
system.debuglog instance
Something to note is that these conversions can appear to be hidden away from a casual reader so use them with care and consideration. Often explicit code, like the ToString example above aids a reader in understanding the code because its right there in front of them that you are asking the class to give you a string representation for some purpose.
Keep that in mind and use these with that in mind.
A recent comment on my post about overriding prompted me to write this second post about interfaces and why you might use them. Its clear to me that I havent done a very good job of explaining why you should or would use them.
In some languages you can create classes that inherit from one other and one other only. Xojo is this way. Some others let you inherit from several superclasses but this has a set of issues that Xojo avoids by limiting things to single inheritance. Java is also this way and does not support multiple inheritance.
So what do you do in the case you have a class you’ve created and you want it to “inherit” from multiple other classes ? Suppose you have a class that needs to inherit from “InputStream” so it can be read from AND also needs to inherit from “OutputStream” so it can be written to ? Xojo has such a class – BinaryStream.
The way to achieve this is actually NOT to try & inherit from two super classes but to make the InputStream and OutputStream into interfaces and then have your class implement both interfaces. In fact this IS what BinaryStream does – it implements both the Readable and Writeable interfaces so you can read from it and write to it.
You implement the methods on each interface and then any other methods on your code that takes a Readable parameter you can pass a BinaryStream to, since BinaryStream IsA Readable. And any of your code that takes a Writeable parameter you can also pass the BinaryStream to since it IsA Writeable.
As well you can extend interfaces just like any other class.
So its perfectly legal to write a method like
Sub DoREadableStuff(extends instance as Readable)
just as you would for any other type in Xojo.
This gives you a lot of flexibility in how you use & implement interfaces.
About the only thing you might run into is what to do if you have two interfaces that you want a class to implement and they have methods that you have to implement that are named the same ?
As long as the full signature of the methods, method name and all parameters, are unique this isnt a problem as you can implement both and you will just appear to have 2 or more overloads of the same method just taking different parameters.
But what if they have the same parameters ?
If you just add both interfaces then you will have to methods with the same name and the same parameters and Xojo will not compile your program any more because its ambiguous which one would be used when.
And this is where a little used aspect of Xojo comes into use.
Lets set up a small example so you can follow along as that will make things much clearer.
Start a new desktop project.
To the project add
a class named class1
a class interface named interface1
add a method to interface1 named Connect
a second class interface named interface2
add a method to interface2 also named Connect
Now we’re going to make it so class1 implements both interface1 and interface2. Click on Class1 and make sure the Inspector is showing. Click on the Interfaces button in the Inspector. Select both Interface1 and Interface2. I often also select the “insert #pragma error” in each method option at the bottom of the pane.
Click OK and empty stubs for all the methods in Interface1 and Interface2 will be added to Class1. And note we do have 2 connect methods.
If you try to run or analyze the project you will get a “duplicate method definition” error – this is exactly as expected since we have two identical signatures (method name and parameters)
And now we’ll fix this. What we need to tell Xojo is that “this method implements the Connect method from interface1 and this other method implements the Connect method from Interface2”.
Right click on the first Connect method. And select the “Show Implements” option from the contextual menu. Notice that when you do this an extra field shows up in the Inspector.
This extra field is used for exactly what we just stated – telling Xojo which Connect method each of the Connect overloads actually implement. So for the first one we’ll put in the field Interface1.Connect. Then click the second Connect method and in the field put Interface2.Connect
You’d think we’re done at this point because those two method now say which method from which interface they implement. However, they are still exact duplicates and the compiler will still complain about that. So rename the first one Interface1Connect and the second Interface2Connect. I’ve renamed this way JUST for clarity for this example. You could rename them anything you liked that doesnt cause other errors.
And now for the proof that this works.
In our sample applications Window1.Open event (or opening for those using 2019r2 and newer) lets put in the following code
Dim i As interface1 = New Class1
break
i.Connect
Dim j As interface2 = Interface2(i)
break
j.Connect
Recall that a Class1 IsA Interface1 because it implements that interface. And that it is also an Interface2 because it also implements that interface. If you run this code now you wont get any errors BUT it is hard to see that it is doing exactly what we’ve told it to.
If you run & step INTO each call to Connect you will see that because in the first case i IsA Interface1 that stepping into i.Connect does indeed call Interface1Connect – the exact method we said implemented the Interface1.Connect method.
And when you get to the second break statement and step into j.Connect you will see that, because j IsA Interface2 that this call to Connect will end up in Class1’s Interface2Connect method – the one set said implemented Interface2.Connect.
Sometimes when you create a subclass you want to your subclass to do something different than the class it inherits from. But you want to retain the API or reuse the method name because its perfectly suited.
When you do this and add a method that has the EXACT SAME signature as the one in the superclass you are overriding the method. The signature includes the method name, parameters and their types but excludes the return type.
This can be very useful if you have a class that has many specialized subclasses. For instance, the framework has several subclasses of TCP Sockets; HTTPSockets, SMTPSockets and POP3Sockets. What an HTTP Socket does when you call it Connect method may be different than what the SMTP Socket does when you call its Connect method and also different from what the POP3 Socket does when you call its connect method.
Suppose you have classes like :
Class BaseClass
Sub Foo()
System.debuglog CurrentMethodName
End Sub
End Class
Class SubClassA
Inherits BaseClass
Sub Foo()
System.debuglog CurrentMethodName
End Sub
End Class
Class SubclassB
Inherits BaseClass
Sub Foo()
System.debuglog CurrentMethodName
End Sub
End Class
And code that uses these classes as
Dim c As BaseClass
c = New SubClassA
c.foo
c = New SubclassB
c.foo
What you will see in the debuglog is something like this on macOS.
10:09:20 PM : My Application Launched
SubClassA.Foo
SubclassB.Foo
10:09:27 PM : My Application Ended
Whats going on ?
Methods are, unlike properties, virtual. What that means is that the correct method to call will be determined NOT at compile time, but at runtime and based on the actual instance and its method list. This is really handy in lots of ways as I already noted.
You can, as we did in our code, declare the type as the most generic – like we did – since all subclasses are instance of the subclass AND whatever the superclasses that its inherited.
In our code above an instance of SubClassA is both a SubclassA AND a BaseClass.
There are some glitches that you can still run into. For instance this bug report notes one spot where the correct method to call is NOT based on the runtime type but the compile time type (which is really counter intuitive)
That said, overriding is very useful in a class hierarchy.
If you have CSV data that is in and encoding other than ASCII you CAN still use the CSV parser but the results you will need to use “define encoding” on
See this post https://forum.xojo.com/57109-csv-parser-with-utf8-support
Xojo doesnt support named parameters. And I dont expect they are likely to add it any time soon. But, you can kind of fake it for yourself with a bit of work.
Xojo supports both variadic parameter lists, ones that take an unbounded list of parameter, and a declarative syntax for creating pairs as literals.
You can combine this into faking named parameters.
If you define a method as
Sub foo( paramarray namedParams as Pair )
You can call it like
Foo( "param1":1, "param2":"2" )
and the method will get an array of pairs named namedParams that is composed of pairs. Each pair in the list will, for this example, have its left property holding the name and the right property holding he parameter value.
You can iterate over the array and pull out the relevant values into local variables and the method behaves as if you support “named” parameters like :
dim param1 as integer
dim param2 as integer
for each param as pair in namedParams
select case true
case param.left = "param1"
param1 = param.right.integervalue
case param.left = "param2"
param2 = param.right.stringvalue
else
// here ypou cane do whatever you want
// but I chose to raise an exception
Raise new UnsupportedOperationException
end select
next
You could take this further and add an overload that supports positional parameters like
Sub foo( param1 as integer, param2 as string )
and if you do this you probably want to make sure the named parameter version validates that the types of values for the named versions are consistent with the positional version.
And now you can call with named params or positional params and you can decide whether internally the named one calls into the positional param one or vice versa so you dont have to write the same code twice – just put a different API on it.
Flat out I’ll say Thanks to Xojo for the 2.1 decision to revert the event name changes. Of all the changes that one was probably the hardest one to deal with as there’s no easy way to #if around an event definition or event handler.
Properties may still cause some grief if you start a project in 2019r2 or r2.1 since they don’t retain the “old” values” so going back to an old version can be tricky if you start in 2019r2 or 2.1. At least this can be worked around by starting projects in 2019r1.1 or older and its fairly simple to make it so every project behaves like this by using a template that overrides the normal built in one.