Showing a different picture on hover and changing the mouse cursor

I’m really stuck trying to create a sort of button that changes its image when the mouse hovers over it.
Ideally, the mouse cursor should change from the default arrow to a hand, just like when hovering over a link.

This is my first project with Web2; I used Web1 some years back.
I’ve tried using JavaScript and CSS -both are new to me- and found nothing current for Web2 online.
ChatGPT wasn’t very helpful either, leading me into dozens of dead ends.

I think this should be done with CSS, but I still have no clue how.

Any help would be greatly appreciated.

Read here:

this is probably what you are searching (command name: hover).

Will do!
In the meantime I managed to fabricate somethin ugly, but almost working: a WebContainer with a htmlviewer and some html/css code at .opening. I just have to figure out how to add a ‘pressed’ event.
I’m sure there is something more elegant, but it’s a start.

I add this in the opening event of a label if I want to show another mouse pointer on hovering over the label:

me.Style.Value("cursor") = "pointer"

1 Like

I wrote a Method that applies WebStyles and GraffitiStyles to a WebTextField/WebTextArea/GraffitiTextField by passing the existing Style plus the items you want to change, including the Pointers. You might be able to adapt this for a WebButton Style:

Protected Sub doTextStyle(myObject As Object, CurrentStyle As Object, StyleName As String = "", FontName As String = "", FontSize As Single = -1, ForegroundColor As Integer = -1, BackgroundColor As Integer = -1, BorderColor As Integer = -1, BorderThickness As Integer = -1, Opacity As Integer = -1, isBold As Integer = -1, isHasHorizontalScrollbar As Integer = -1, isItalic As Integer = -1, isStrikethrough As Integer = -1, isUnderline As Integer = -1, LineHeight As Single = -1, LineSpacing As Single = -1, NewCursor As String = "")
  #If TargetWeb Then
    Var GraffitiStyleContent As String
    Var isGraffitiStyle As Boolean = False
    
    Var tempWebStyle As New WebStyle
    Var isWebStyle As Boolean = False
    
    If CurrentStyle <> Nil And CurrentStyle IsA WebStyle Then
      tempWebStyle = WebStyle(CurrentStyle)
      isWebStyle = True
      
    ElseIf CurrentStyle <> Nil And CurrentStyle IsA GraffitiStyle And StyleName <> "" Then
      isGraffitiStyle = True
      
    Else
      Return
    End If
    
    '!important is used to stop it being overridden by the Bootstrap theme 'graffitisuite.com/support/#/ticket/537/view
    If BackgroundColor >= 0 Then '&cFFFFFF
      tempWebStyle.BackgroundColor = CommonPictures.getColorFromInteger(BackgroundColor)
      GraffitiStyleContent = GraffitiStyleContent + "background:" + CommonHTML.getHTMLFromColorRGB(CommonPictures.getColorFromInteger(BackgroundColor)) + " !important;"
    End If
    If BorderColor >= 0 Then '&cFFFFFF
      tempWebStyle.BorderColor = CommonPictures.getColorFromInteger(BorderColor)
      GraffitiStyleContent = GraffitiStyleContent + "bordercolor:" + CommonHTML.getHTMLFromColorRGB(CommonPictures.getColorFromInteger(BorderColor)) + " !important;"
    End If
    If BorderThickness >= 0 Then
      tempWebStyle.BorderThickness = BorderThickness
      GraffitiStyleContent = GraffitiStyleContent + "border=" + BorderThickness.ToString + "px !important;"
    End If
    
    If isGraffitiStyle And NewCursor <> "" Then
      GraffitiStyleContent = GraffitiStyleContent + "cursor:" + NewCursor.Lowercase + " !important;"
      
    ElseIf isWebStyle Then
      Select Case NewCursor
      Case ""
        'do nothing
      Case "CrossHair"
        tempWebStyle.Cursor = WebStyle.Cursors.Crosshair
      Case "Default"
        tempWebStyle.Cursor = WebStyle.Cursors.Default
      Case "Help"
        tempWebStyle.Cursor = WebStyle.Cursors.Help
      Case "Inherit"
        tempWebStyle.Cursor = WebStyle.Cursors.Inherit
      Case "Move"
        tempWebStyle.Cursor = WebStyle.Cursors.Move
      Case "NoDrop"
        tempWebStyle.Cursor = WebStyle.Cursors.NoDrop
      Case "None"
        tempWebStyle.Cursor = WebStyle.Cursors.None
      Case "NotAllowed"
        tempWebStyle.Cursor = WebStyle.Cursors.NotAllowed
      Case "Pointer", "Finger"
        tempWebStyle.Cursor = WebStyle.Cursors.Pointer
      Case "Progress"
        tempWebStyle.Cursor = WebStyle.Cursors.Progress
      Case "ResizeColumn"
        tempWebStyle.Cursor = WebStyle.Cursors.ResizeColumn
      Case "ResizeEast"
        tempWebStyle.Cursor = WebStyle.Cursors.ResizeEast
      Case "ResizeNorth"
        tempWebStyle.Cursor = WebStyle.Cursors.ResizeNorth
      Case "ResizeNorthEast"
        tempWebStyle.Cursor = WebStyle.Cursors.ResizeNorthEast
      Case "ResizeNorthWest"
        tempWebStyle.Cursor = WebStyle.Cursors.ResizeNorthWest
      Case "ResizeRow"
        tempWebStyle.Cursor = WebStyle.Cursors.ResizeRow
      Case "ResizeSouth"
        tempWebStyle.Cursor = WebStyle.Cursors.ResizeSouth
      Case "ResizeSouthEast"
        tempWebStyle.Cursor = WebStyle.Cursors.ResizeSouthEast
      Case "ResizeSouthWest"
        tempWebStyle.Cursor = WebStyle.Cursors.ResizeSouthWest
      Case "ResizeWest"
        tempWebStyle.Cursor = WebStyle.Cursors.ResizeWest
      Case "Text"
        tempWebStyle.Cursor = WebStyle.Cursors.Text
      Case "VerticalText"
        tempWebStyle.Cursor = WebStyle.Cursors.VerticalText
      Case "Wait"
        tempWebStyle.Cursor = WebStyle.Cursors.Wait
      Case Else
        Break
      End Select
    End If
    
    If FontName <> "" Then
      tempWebStyle.FontName = FontName
      GraffitiStyleContent = GraffitiStyleContent + "font-family:" + FontName + " !important;"
    End If
    If FontSize >= 0 Then
      If FontSize = 0 Then 'default
        #If TargetDesktop Or TargetiOS Then
          FontSize = 12
        #ElseIf TargetWeb Then
          FontSize = 16
        #EndIf
      End If
      tempWebStyle.FontSize = FontSize
      GraffitiStyleContent = GraffitiStyleContent + "font-size:" + FontSize.ToString + "px !important;"
    End If
    If ForegroundColor >= 0 Then '&cFFFFFF
      tempWebStyle.ForegroundColor = CommonPictures.getColorFromInteger(ForegroundColor)
      GraffitiStyleContent = GraffitiStyleContent + "color:" + CommonHTML.getHTMLFromColorRGB(CommonPictures.getColorFromInteger(ForegroundColor)) + " !important;"
    End If
    If isBold >= 0 Then
      tempWebStyle.Bold = If(isBold = 1, True, False)
      GraffitiStyleContent = GraffitiStyleContent + "fontWeight='bold'  !important;;"
    End If
    'If isHasHorizontalScrollbar >= 0 Then
    ''tempWebStyle.Bold = If(isHasHorizontalScrollbar = 1, True, False)
    'GraffitiStyleContent = GraffitiStyleContent + "white-space: pre !important; overflow: auto !important;" 'graffitisuite.com/support/#/ticket/540/view
    'End If
    
    If isItalic >= 0 Then
      tempWebStyle.Italic = If(isItalic = 1, True, False)
      If isItalic = 1 Then
        GraffitiStyleContent = GraffitiStyleContent + "fontstyle:'italic' !important;"
      End If
    End If
    If Opacity >= 0 Then '0=Invisible to 100=fully opaque
      tempWebStyle.Opacity = Opacity
    End If
    If isStrikethrough >= 0 Then
      tempWebStyle.Strikethrough = If(isStrikethrough = 1, True, False)
      If isStrikethrough = 1 Then 'object.style.textDecoration = "none|underline|overline|line-through|blink|initial|inherit"
        GraffitiStyleContent = GraffitiStyleContent + "textdecoration:'line-through' !important;"
      End If
    End If
    If isUnderline >= 0 Then
      tempWebStyle.Underline = If(isUnderline = 1, True, False)
      If isUnderline = 1 Then
        GraffitiStyleContent = GraffitiStyleContent + "textdecoration:'underline' !important;"
      End If
    End If
    If LineHeight >= 0 Then 'on Desktop LineHeight = pixels, but on Web LineHeight = XXX%
      'tempWebStyle.LineHeight = LineHeight
      If LineHeight = 0 Then
        LineHeight = 1 'default is 100%
      End If
      GraffitiStyleContent = GraffitiStyleContent + "line-height:" + CommonStrings.getNumberAsPercentageWAD(LineHeight, 1, 1, 1000) + " !important;"
    End If
    'If LineSpacing >= 0 Then
    ''tempWebStyle.LineSpacing = LineSpacing
    'If LineSpacing = 0 Then
    'LineSpacing = 1 'default is 100%
    'End If
    'GraffitiStyleContent = GraffitiStyleContent + "line-spacing:" + CommonStrings.getNumberAsPercentageWAD(LineSpacing, 1, 1, 1000) + " !important;"
    'End If
    
    If myObject IsA WebTextField Then
      WebTextField(myObject).Style = tempWebStyle
    ElseIf myObject IsA WebTextArea Then
      WebTextArea(myObject).Style = tempWebStyle
    ElseIf myObject IsA GraffitiTextField Then
      Var tempGraffitiStyle As GraffitiStyle = GraffitiStyle.Create(Session, StyleName, GraffitiStyleContent)
      GraffitiTextField(myObject).FieldStyle = tempGraffitiStyle
    Else
      Break
    End If
  #EndIf
    
End Sub
3 Likes

Up to now I managed to get this far:
Created a webcontainer with the x, y of the wanted image and hovered image.
In the webcontainer with the same size a webhtmlviewer.

Opening event in the webcontainer:

Me.Style.Value("cursor") = "pointer"

Opening event in the htmlviewer (cls_Webpics is a global class for the webpictures in the hope this will reduce memory use):

Var html As String
html = "<div class='image-container'>" + _
"<img class='default-image' src='" + cls_Webpics.b_AntiAging_n.URL + "' />" + _
"<img class='hover-image' src='" + cls_Webpics.b_AntiAging_h.URL + "' />" + _
"</div>" + _
"<style>" + _
".image-container { position: relative; width: 100%; height: 100%; }" + _
".default-image { position: absolute; width: 100%; height: 100%; transition: opacity 0.3s ease-in-out; }" + _
".hover-image { position: absolute; width: 100%; height: 100%; opacity: 0; transition: opacity 0.3s ease-in-out; }" + _
".image-container:hover .default-image { opacity: 0; }" + _
".image-container:hover .hover-image { opacity: 1; }" + _
"</style>"
Me.LoadHTML(html)

This works so far. Hope the speed will not be that bad when the 10 needed controls are in place.
One showstopper left: How do I implement a click event for that?
All the suggestions from ChatGPT were, well, crap. And I am not familar enough with css/js to do it myself.
Playing with webbutton got me nowhere. That does not mean there is no way with webbutton, but i could not figure it out.

Something like this?

All I did was, in App HTML Header:

<style>
.hover-img:hover {
  background:url('https://i.ibb.co/CW5Wvry/buttonpng.png');
  background-size: 100% 100%;
}
</style>

Button CSS:

I think Xojo 2024r3+ is needed for the CSS section.

1 Like

Will try today. At least I now have an idea how to use own css classes.

Hi @Rolf_Genster,

If you want to handle Pressed events, the above WebButton example by @AlbertoD will be easier than trying to do it with a HTMLViewer.

As for the mouse cursor, it will be better to just use what the framework already give you, to avoid typos:

Me.Style.Cursor = WebStyle.Cursors.Pointer

There is no hover support at the moment, but please feel free to create or upvote related Feature Requests.

2 Likes

A follow up:
I abandoned my tries with html-viewer or button. The simplest approach is the WebImageViewer, since it has a .pressed event:
In .opening i applied:

Me.Style.Value("cursor") = "pointer"
Me.Style.Value("background-image") = "url('" + cls_Webpics.b_AntiAging_n.URL + "')"

Still I didn’t manage to implement a hover css. For example

Me.Style.Value("hover") = "url('" + cls_Webpics.b_AntiAging_h.URL + "')"

or

Me.Style.Value(":hover") = "url('" + cls_Webpics.b_AntiAging_h.URL + "')"

do not work , and i did not find other css options at w3school.
Any further ideas?

Oh, I was just writing.
Okay, feature request filed: https://tracker.xojo.com/xojoinc/xojo/-/issues/78054
But is there no other possibilty in the meantime?

I don’t think it’s going to work that way because the “Value” part is one of the CSS property names, and the :hover directive is part of the CSS selector itself (what the framework is returning for me.Style).

I think a ticket for better hover support in general (you can ask them to rename your ticket) would be the most “in framework” route forward. There are some Web 2.0 wizards in this thread already, so in time someone might have a suggestion for an interim workaround.

Create the CSS in App HTML Header and add it to the button/ImageViewer, you can use the code:

me.CSSClasses.Add("hover-img")

If you provide an example project with the b_antiaging_n and b_antiaging_h URLs (you can zip it and upload to the forum), I can create an example for you.

Edit: Sample:
HoverImage.xojo_binary_project.zip (7.9 KB)

GIF:
screencast2024-12-1010AM-38-32-ezgif.com-video-to-gif-converter

2 Likes

Just had extracted an example project :grinning:

Well, thanks a lot!

There are two things I have to figure out though:

  1. The HTML-Header will be cramped with these slightly different styles since there a 20 of them (and i fear there will be lots more).
  2. This way another webserver has to serve the pictures. I am trying to build a standalone webserver that serves these pictures himself, but the url generation of a webpicture is generic, not?

So another question regarding style section, now in app.htmlheader:
Is it possible to use variables or implement the style section of into the control itself?

Again, many thanks to all for their input here, helps me a lot to learn the basics for web 2.0.

Changed the subject.

Ahhh, got it:
After initialising my picture class in app.opening and adding the needed code with the dynamic links for the webpictures to app.htmlheader this should work.
Edit: No, app.htmlheader is read only. Too bad.

i believe it was here

Perfect, thank you. I never dared to look there.
I’ll give it a try tomorrow, grandchildren-time starts now.

1 Like

So, finally, there is a working solution for this task.

Here is the hover example with self-hosting images, if someone ist interested:
HoverTest.zip (22.2 KB)

If you also like to have this directly implemented in xojo please give it a thumbs up:
https://tracker.xojo.com/xojoinc/xojo/-/issues/78054

2 Likes

btw i just found a input for html header too in the UI