Make External?!?

fromTOF for @johnmcsoft

if you have images that are needed you do NOT need to drag them into your project to use them
What this gets you a a nice simple way to do that BUT it comes at cost (as youve noticed)
You get a gigantic resources file

You can still use all the images without dragging them INTO the project

there are some things we can do to make this easier

I’m open to suggestions. There are multiple resolutions for almost all of the images, of course…

First things first
When you drag an image into you project what is happening ?
Xojo does a bunch of work for you to make it so when you use that pictures NAME everything just works
The actual details aren’t super important and they could be done in any one of several ways
What if when you run your project Xojo creates a “module” for you from all those pictures.
And in there it has a method for each of your pictures that returns a “multi image” if there are 1 2 or 3 different resolutions.
That would work but Xojo would probably need to make sure all the actually bitmaps etc for those images get copied to the runtime in a way that it can be sure they all exist
So it does that.

And you have little , or not, control over this.
Its why you will see many users recommending NOT doing this (it has other undesirable effects as well)
As John found , it makes for a giant Resources File, that you cannot check into git.

So what if instead of relying on that bit of magic, and giant resources file, we did this for ourselves ?
Its not hard - just takes al little code.
And you can avoid the giant resources file
But you WILL want to add a “copy files step” to make sure all your images get copied into the app bundle

give me a few minutes to stub out a way to AVOID the giant resources file

copy files step is the easy part…

OK so here as REALLY simplified setup

a project with one image that is set to be the backdrop of a canvas
there is no code

this is what we’ll work towards replacing in a way that makes sense
there ARE some things we dont have a way to work around - its just the way the IDE works that makes this necessary

Right…

First we’ll add a module to hold all our code
I called mine ResourceImageAccess
And, since I need to replace what Xojo does with my picture IO need a method named

Beeker_msn() as picture

so thats added as well

and at this point if i run things still run - my method is NOT used yet

So I need to

  1. add a copy files step to copy my image into the resources dir (maybe a sub dir)
  2. write code that will find the resources folder in the app
  3. and load an image into a multi image by name

Ah! That’s actually pretty straightforward, it just requires some advance planning. I’m packing for a trip tonight, will give this a go in the morning. THANK YOU!

the copy files step is pretty easy

create one and make sure the destination is set to resources (and maybe a subdir as well which I will use for this example)

so now when we run of build the images will be in Resources > images

on to the code
Add a method to the ResourceImageAccess module

Private Function findImagesDir() As folderitem
  
  Dim f As folderitem = SpecialFolder.Resources
  
  If f Is Nil Or f.exists = False Then
    Return Nil
  End If
  
  // note this should match the SUBDIR name from the copy file step !!!!!!
  If f.Child("Images") Is Nil Or f.Child("Images").Exists = False Then
    Return Nil
  End If
  
  Return f.Child("Images")
End Function

This will locate the dir where the copy file step put all the images

Now we need to create a “multiimage”
Add the following method to the module
This method will look for png & jpg images
It also defaults to looking for the 1x WITHOUT any @1x , and the 2x and 3x with @2x and @3x suffixes to the base name( normal macOS naming for image sets)

Private Function multiimage(baseName as string) As picture
  Dim imageRoot As folderitem = findImagesDir
  
  If imageRoot Is Nil Then
    Return Nil
  End If
  
  Dim imageFolderitem As folderitem
  
  Dim images() As picture
  
  // lets see what we can find - we can / could use png, jpg - we'll prefer pngs
  
  // 1x image
  imageFolderitem = imageRoot.Child(basename+".png")
  If imageFolderitem Is Nil Or imageFolderitem.exists = False Then
    imageFolderitem = imageRoot.Child(basename+".jpg")
  End If
  If ((imageFolderitem Is Nil) = False) And (imageFolderitem.exists = True) Then
    images.append Picture.Open(imageFolderitem)
  End If
  
  // 2x image
  imageFolderitem = imageRoot.Child(basename+"@2x.png")
  If imageFolderitem Is Nil Or imageFolderitem.exists = False Then
    imageFolderitem = imageRoot.Child(basename+"@2x.jpg")
  End If
  If ((imageFolderitem Is Nil) = False) And (imageFolderitem.exists = True) Then
    images.append Picture.Open(imageFolderitem)
  End If
  
  
  // 3x image
  imageFolderitem = imageRoot.Child(basename+"@3x.png")
  If imageFolderitem Is Nil Or imageFolderitem.exists = False Then
    imageFolderitem = imageRoot.Child(basename+"@3x.jpg")
  End If
  If ((imageFolderitem Is Nil) = False) And (imageFolderitem.exists = True) Then
    images.append Picture.Open(imageFolderitem)
  End If
  
  
  Try
    Dim multiPicture As New Picture(images(0).width,images(0).height, images )
    
    Return multiPicture
  Catch iax As InvalidArgumentException
  Catch noe As NilObjectException
  Catch oom As OutOfMemoryException 
  End Try
  
  Return Nil
  
End Function

And now Beeker_msn, in my case, is

Public Function Beeker_msn() As picture
  
  Return multiImage( "Beeker_msn" ) 
  
End Function

Now for the glitches
With images dragged into the IDE you can set backdrops etc in the IDE by selecting an image
That will not work with this mechanism. It cant. The IDE has no idea about our custom mechanism

So anywhere we had set a property we will need a couple lines of code to set the property

In this screen shot I’ve cloned the canvas so the one on the left uses an image dragged into the project and the one on the right will set the backdrop property

And so the open event of the right hand canvas reads

Me.Backdrop = Beeker_msn
me.invalidate
  • now you may notice that the image I have is named Beeker_msn1 and my method Beeker_msn.
    In order to do this demo I cant name them EXACTLY the same as things wont work

And now I cant simply add whatever methods I need to the module and remove the images dropped into my project and get rid of the giant resources file.

That’s pretty slick! I hope you’re well and have work, thanks again :+1:

You may want to use a static variable in the Beeker_msn method to cache the picture.
Or a dictionary in multiimage() to cache them all.

Oh sure
This was just “the basics”
After you get this down you can do al kinds of things

@johnmcsoft
You may want to mention on TOF that you got your issue addressed outside the forum (since you can’t mention this place over there), but it was solved nonetheless. Cause the responses you got there pale in comparison to what you got here - I mean is not even close

1 Like

he didnt even get a reply there until this morning

now the resource file being HUGE is screwy unless you have numerous icons, cursors etc

I suppose you could delete it then reset the app icon

FWIW, I’m recovering from retina surgery, will be looking at all of this in a few weeks. Thank you!

I hope you are not alone as this is a difficult time if the other eye is not > 5/10°…

I wish you a good recovery.

Fortunately, my husband and our dog are here, it’s going well and the doctor sounds very pleased so far. Thank you!

2 Likes

Got surgery good news today, complete and better than expected results, I may have full vision within the week!

4 Likes

Glad to read that.

I am waiting for new glasses.