Coincidence

Sometimes you want classes to have a common API.

But rather than adding a common super class or interface to define that common API, you just make sure your classes have the same methods, events, properties & whatever else you feel is necessary to make them have “the same API”.

I would suggest you rethink doing this.

If what you need is controls that are similar but have differences in them just making it so you have common methods names, properties, and events will make trying to write any common code that can manipulate these controls a pain in the read end to create, maintain, and update.

For instance, lets suppose we create two controls that do wildly different things. One is a canvas control subclass that draws itself in whatever color set with a blue frame. The other draws the frame in in red. (This example is exceeding simple just to illustrate the issues. Its worse with more complex classes & controls)

So we have

Class BlueCanvas
  Inherits Canvas

  property myfillColor as color

  Sub SetFill(newFillColor as Color)
     myfillcolor = newFillColor
  End Sub

  Function GetFill() as Color
     return myfillColor
  End Function

  Event Paint(g as graphics, areas() as 
     g.forecolor = myfillColor
     g.fillrect 0,0, g.width, g.height

     g.forecolor = &c0000FF
     g.drawrect 0,0, g.width, g.height
  End event
End Class

Class RedCanvas
  Inherits Canvas

  property myfillColor as color

  Sub SetFill(newFillColor as Color)
     myfillcolor = newFillColor
  End Sub

  Function GetFill() as Color
     return myfillColor
  End Function

  Event Paint(g as graphics, areas() as 
     g.forecolor = myfillColor
     g.fillrect 0,0, g.width, g.height

     g.forecolor = &cFF0000
     g.drawrect 0,0, g.width, g.height
  End event
End Class

So our two controls have “the same API”

Same events, same methods, same properties of the same type & name.

Lets see what happens when we try to write code that uses these two simple controls.

Suppose we’d like to write a single method that can change the fillcolor of either control

Sub AlterFillColor(ctrl as Variant)

   Dim c As Color 

   If SelectColor(c,"select a color") Then
  
    If ctrl IsA BlueCanvas Then
      BlueCanvas(ctrl).myFillColor = c
      BlueCanvas(ctrl).Invalidate
    Elseif ctrl IsA RedCanvas Then
      RedCanvas(ctrl).myFillColor = c
      RedCanvas(ctrl).Invalidate
    End If
  
End If

Note several things. The parameter passed in has to be something generic – but it cant be a BlueControl or a RedControl as there is nothing really common between them. We could pass a Canvas but that would not change the code in actual method.

We MUST check to see, in this case, exactly what type was passed in because we cant be sure an appropriate type was passed in. Even if we used a Canvas we’d have to check because a generic Canvas may not have the myFillColor property and trying to access that would cause compilation errors. If we use Variant anything can be passed in. If we make that parameter an Object any INSTANCE of ANY class, not JUST our labels, can be passed in.

We MUST cast, either one time and assign to a temporary we reuse , or on every line of code where we use the control.

And this is one VERY short method and it already looks messy.

How about adding a button to copy the value of myFillColor from one canvas to another.

If we add one button to copy from the bluecanvas to the redcanvas, and another to copy the other way we can copy directly from one to another using something like :

RedCanvas1.SetFill( BlueCanvas1.GetFill() )

or

BlueCanvas1.SetFill( RedCanvas1.GetFill() )

This isnt so bad. But, being the clever sort we are we decide to write a generic “copy from one to the other” method and reuse the heck out of it (and this is where your spidey sense should really go crazy !)

Public Sub CopyFill(fromCtrl as variant, toCtrl as Variant)

  Dim copycolor As Color

  If fromCtrl IsA BlueCanvas Then
    copycolor = bluecanvas(fromctrl).FillColor
  Elseif fromCtrl IsA redCanvas Then
    copycolor = redcanvas(fromctrl).FillColor
  End If

  If toCtrl IsA BlueCanvas Then
    bluecanvas(toCtrl).FillColor = copycolor
  Elseif toCtrl IsA redCanvas Then
    redcanvas(toCtrl).FillColor = copyColor
  End If
  
End Sub

And from the outset we have the same issue as above

Except now to manipulate two items we need to do several checks to see what types the from and to controls are.

So despite these “having the same API” thats of no real value since you have to cast and check all the time. And your parameters to methods have to be some more generic type like variant or some common super that still leaves you needing to cast all the time.

Worst of all you can pass ANY parameter that is of the right type. The compiler cannot help you check that your code is correct since it cant do type checking.

Now what if we decide we want to add a … YellowCanvas !

You can go back and see for yourself how many places we have to adjust to handle this “new” type. Every place we have a cast is a potential place we need to add code to.

And every place we add code is a new place for new bugs to crop up.

So how can we solve this in our little example ? I’m sure many of you have already seen the answer. In this case an interface ISNT of use since we can’t define events and properties.

But a common base class, even one that CANNOT be created directly, is of use.

Class FramedCanvas
  Inherits Canvas

 property myfillColor as color

  Sub SetFill(newFillColor as Color)
     myfillcolor = newFillColor
  End Sub

  Function GetFill() as Color
     return myfillColor
  End Function

  Private Sub Constructor()
    // makes it so you cant place instances on a layout
    // or create them direcly
  End Sub

End Class

Class BlueCanvas
  Inherits FramedCanvas

  Event Paint(g as graphics, areas() as 
     g.forecolor = myfillColor
     g.fillrect 0,0, g.width, g.height

     g.forecolor = &c0000FF
     g.drawrect 0,0, g.width, g.height
  End event
End Class

Class RedCanvas
  Inherits FramedCanvas
  
  Event Paint(g as graphics, areas() as 
     g.forecolor = myfillColor
     g.fillrect 0,0, g.width, g.height

     g.forecolor = &cFF0000
     g.drawrect 0,0, g.width, g.height
  End event
End Class

Now our classes dont just have the same API because we happened to write them that way. They have the same API by definition. They cant NOT have the same API.

Now our method to set a fillColor looks like

Public Sub AlterFillColor(ctrl as FramedCanvas)
  Dim c As Color 
  
  If SelectColor(c,"select a color") Then
    
    ctrl.FillColor = c
    ctrl.Invalidate
  End If
  
End Sub

Note we CAN use the super class as the TYPE for the parameter. BlueCanvases are also FramedCanvases. As are RedCanvases. But a regular canvas control could NOT be passed in (unlike before) and we do not need to do any casting etc. The compiler CAN check for us what is passed in. So our reliability and likelihood of writing bug free code is improved.

And, note, we have LESS code than before. And even if we add YellowCanvases etc we do NOT need to alter this code. Thats a good thing.

Directly copying the fill color from one control to another still works about the same

BlueCanvas1.SetFill( RedCanvas1.GetFill() )
BlueCanvas1.Invalidate

or

RedCanvas1.SetFill( BlueCanvas1.GetFill() )
RedCanvas1.Invalidate

What about our method based copy that we want to just reuse like mad ?

Public Sub CopyFill(fromCtrl as FramedCanvas, toCtrl as FramedCanvas)
  toCtrl.FillColor = fromCtrl.fillColor 
End Sub

Its trivial. No casts. No need to alter it every time a new sub type of framedCanvas comes along. And unlikely to ever have a bug.

Why ? Because these controls have an EXPLICIT API that forces them to be the same. Its not just because I happened to write the same code in several of them but because BY DEFINITION they are the same.

Where’s this all leading ?
Well so far this has all been simple and in a desktop app. It could just as well have been in a web app, iOS or some other app type. But the PRINCIPLES hold true even if you wanted to extend this to be across all those app types.

Suppose the hierarchy defined was

  Control
     - common control events, method & properties

     RectControl
         - common rect control events, method & properties 
           (for controls bound by a bounding box)
         Button
            - common button events properties & methods
            DesktopButton
               - events properties & methods specific to the desktop
            WebButton
               - events properties & methods specific to the web
            iOSButton
               - events properties & methods specific to ios            
         Canvas
         Listbox
         TabPanel
         PagePanel

and so on

Now its plausible to write a method that regardless of whether it was used in a desktop, ios, or web project that could locate a Control, Rectcontrol, or Button. Or any other control that is defined.

No casting required.

There are some compatibility details. The DesktopButton would have to have its flags set to only be in desktop projects, the web ones only in web projects, etc. That would solve trying to compile a desktop project with iOS or web controls & vice versa.

The same may be true for controls that only exist on the desktop, and have no counter parts elsewhere. But those flags can be set at any level in the hierarchy.

And the IDE could check those flags to see what things ARE compatible and not allow you to use ones that arent.

The other upside with a properly laid out hierarchy like this for EVERYTHING is that it BY DEFINITION it tells you what properties you could expect to carry over if you copied a desktop button and pasted it into a web project. Nothing that is desktop specific should, or could, make sense in the web one. But anything defined in BUTTON and higher up in the hierarchy would, or should, be able to be copied over to a web button when you paste.

Currently its hard for any of us to do this in a cross platform app that targets ios, web and desktop simply because the underlying items in the frameworks do not do this already.

And the IDE doesnt seem to sort things out right. Depending on what version of the IDE you use you might see in the example SameXPLATExplicitAPI that the Blue Canvas has its compat flags all off but thats really wrong since it SHOULD be compatible with desktop. It would be subclassed from the Desktop one. And using those modules on ios would be from the IOS one. And on web from the web one. But this doesnt work as I expect. In some versions they are all greyed out and things “run” but the paint events never get called 🙁

So trying to make a FramedCanvas, like above, that works in iOS, desktop, and web IS, at the moment, not possible.

Making it so things share as much of their API by DEFINITION rather than just by coincidence would make it so more code is, or can be, cross platform without needing to copy paste from project to project.

Imagine THAT world !

Examples

When extension methods aren’t a suitable replacement

A lot of API 2 is extension methods that are replacements for the classic global framework methods.

String.LowerCase is for all intents and purposes the equivalent of Lowercase(string).

Except when its not.

If you happen to have methods that can return a wide variety of types you may not be able to use the extension methods.

For instance if you have something like

Function Foo(NameOfTypeToReturn as string) as Variant
     // this code IS just for illustration purposes !
     select case nameOfTypeToReturn
       case "String"
           return "string"
       case "Double"
           return 1.234
       case "Integer"
           return 99 // yeah that many red balloons !
       else
           return nil
       end if
End Function

and you were to try and do

Dim s As String

s = Foo("Double").Lowercase

s = Lowercase(Foo("Double"))

only one of these will compile. It’s NOT the extension method style.

And it makes perfect sense why not.

An extension method has a specific signature.

Lowercase is defined like

Function Lowercase(extends str as string) as String

essentially for this to MATCH the parameter to it has to already BE a string. But the return value from Foo is a variant – that can be turned into a string – but isnt already a string. So that usage “doesnt match”. An dyou get a compilation error.

The second however will accept something that can be turned into a string. Which a variant can do. So it compiles just fine.

WinUI

Recently there was a blog post about Xojo looking at WinUI as the new foundation for Xojo.
Thats great – except (you knew that was coming didnt you?)
WinUI wont support anything older than Windows 10 at this time (specifically they note Win10, 1809+)
How Xojo handles this will be interesting to watch.

Will they simply abandon Win32 and any users on older versions ? I could see that happening as it would be similar to what happens with old versions of macOS. Then to support old Windows versions you have to use an old version of Xojo.

It may be irrelevant since Windows 7 support officially ended in 2020.

Hopefully MS can ship WinUI as planned and Xojo apps on Windows will get a whole new look and maybe even new controls

http://feedback.xojo.com/case/61410

Misunderstanding the issue

Last night I read a blog by someone who obviously misunderstands why it is INN and some inhabitants are disheartened by where Xojo is at this point.

There are some there who are definitely Xojo haters now. They didnt start out that way. Years of “it’s coming soon” have lead them to not believe those statements. “Show me” has become their mantra. It’s not real until it ships and everything before that should be ignored.

The IDEA of Xojo is truly wonderful – a tool that can tackle many targets & platforms with a consistent language and reasonably similar frameworks for each target. There are going to be differences and especially differences in behaviour – the web being what it is latency is an issue. Mobile/touch devices dont normally support the same kinds of UI operations as desktop does since there are no mouse or other desktop like input devices – usually. So there will be differences.

And many of those denizens on INN DO still continue to use Xojo. Not all. But many.

But INN’s purpose is to focus on cross platform development – so it has topics on different tools (B4J, Java, RemObject, Swift) that Xojo will not permit on their forums. And it does have uncensored commentary on Xojo itself. Again something that Xojo does not permit.

There ARE comparisons to other tools – primarily for those looking to expand their horizons in any direction (ie/ moving to Xojo from Java or moving to Java from Xojo) The languages are similar in so many respects that moving from one to the other is not difficult. And neither is likely a perfect replacement for the other in all cases.

Good cooks , like good carpenters, use many tools and dont try & use one tool for everything. It doesnt usually work if you try to make everything in the microwave. It doesnt all need the same power settings, the same cook time, nor does it all fit. The same is true for carpenters, Try to use a single tool for everything doesnt work well. Hammers make lousy saws, and saw make lousy hammers.

Being able to mix and match the right tool for the right job requires knowing how to use other tools. And, often, such comparisons or discussions about those other tools are not permitted on Xojo’s forums.

INN exists for all these reasons.

So yes you may get unbridled boosterism for some favourite tool – like Xojo or Java or RemObjects or Swift. But you can & will get other perspectives.

Me ?

I still say to people that I use Xojo and post code that uses it. I still like the IDEA of Xojo. And I’ve used it for so long I can bang out something pretty quickly. I do run into issues. I’ve filed some 600+ feature & bug reports and just keep on moving.

I just happen to dislike where Xojo is at today.
I’m allowed to have an opinion 🙂

Deploying Web Apps

I know Xojo just wrote one but to be honest I really like this one

This handy tool makes it so dead easy its crazy.

Even if you just hacve a static web site its dead simple

LifeBoat should be called life saver

Simple. Fast. Not expensive and does what it does without any muss or fuss.

And it looks good doing it.

If you build web apps

Of just about any kind, not just Xojo web apps, LifeBoat is so darned handy you really should get it.

Its so easy to use that today I redeployed a web app several times in a matter of minutes.

Its truly one of those must have tools

Web 2.0 thoughts

Lately I’ve been writing a Web2.0 app and TBH I can see where folks are experiencing issues.

There are omissions, glitches and bugs that need addressing.

But, I also wonder why Xojo hasnt dog fooded Web 2 with Feedback or some other app they use and rely on ? And that would show them the issues developers are facing.

!5 years ago, before I ever joined REAL, they decided to write the IDE using the IDE. Something they hadnt been doing. A practice known as dogfooding.
And from my decade working there I can say that it helped make things easier to see when a user submitted a bug report. And there were a lot of bugs that never went out the door because we used the IDE to write the IDE.

It was a good idea to do this because

The engineers who are developing REALbasic are now having an experience more in line with their customers’ own experience, which gives them a better sense of what does and doesn’t work.

https://www.macworld.com/article/174461/realbasic-45.html

I do wish they’d write something like a web version of Feedback using Web 2. And, if thats not possible then work to make it possible. Feedback doesn’t have that complex a UI (does it?) It should be possible and if its not maybe that says exactly how ready Web 2 is ? It certainly says something about its readiness.

Your thoughts ?

Lots of Xojo projects ? You probably want this

Anyone who has been using Xojo for many releases probably has seen the dreaded “older version” dialog

It was a user request many many many years ago.

And now, at least with web projects, theres a different warning when you open a Web 1 project in a version that no longer supports Web 1 apps.

Well if you have even encoutered these issues and want to know whats the version I shoulduse to open this project you want the macOS QuickLook plugin from Monkeybread

Now lets be clear – MBS has two items named VERY similarly.
There is one that is a plugin FOR macOS.
And one you can use IN Xojo programs to acces QuickLook functions.

We want the first one. The QuickLook plugin for macOS.

Installation is dead simple.

Download the dmg. Open It.

In Finder Go To ~/Library/QuickLook (shift-cmd-G)

If it says “That folder can’t be found” then Go To “~/Library” and create it (the name is case sensitive) Now go to that folder.

And , from the mounted dmg, drag the MBS Xojo QuickLook Plugin.qlgenerator plugin to ~/Library/QuickLook.

Log out and log back in

I tend to use column mode so when I click on a project file I now see

If I right click I can select a specific version of Xojo to open the project with. Or I can use the Get Info window to specify which one should open this project from now on.

If you use other view styles in macOS CMD-Y will sho you the quicklook info for the file.

And you can start to banish those dialogs from you day to day work flows.

Laziness as a good thing

Lots of time I see people say they hack and hack at code until it “works”
But those accumulated hacks and patches can be troublesome down the road as you may not remember why you did something that way

And comments are, for the most part, obsolete the moment you write them.
And, unless you have some kind of enforced policy, either personally or corporately, they probably do not get maintained as code gets updated.

So how to avoid this situation where comments are useful and you cant read the code you wrote 6 months ago because you wrote it in a hurry and didnt bother with good names for anything ?

Quite honestly I find I like to be lazy.

By that I mean – slow down ! Take the time to do it once. And do it right. Or as right as you can make it at the time. Use long descriptive variable names. Give your methods good names. And then at least your code will maybe read more like english and actually self document the intent of the code.

Code you quickly hack together today and have to go back and decipher in a month or more does you no favours. In the long run having to revisit that code more than once will cost you both time & effort. And if it isnt carefully thought out you may introduce subtle side effects by making changes to your code.

Repeated studies have shown that the sooner to find and fix bugs and errors in code the less they cost to fix. Quickly written hacked together code is still early enough in the overall process that it can cost a ton to fix later (if you can fix it at all) Design flaws that arent caught can be horribly expensive to fix once a product rolls out – or worse cant be fixed without such a huge change that for all intents & purposes it will never happen.

My advice. Be lazy. Do it once. Do it right.