Share My Creation Color Picker Tool for Windows - Pick any color from screen and copy HEX or RGB to clipboard

Hi everyone!


I'd like to share a Color Picker tool I built with B4J. It lets you hover the mouse over any pixel on the screen and copy the color value to the clipboard.

colorpicker3.gif


How it works:


  • Run the app (or build it as a standalone .exe using Project > Build Standalone Package)
  • A small overlay panel will follow your mouse showing a zoom of the pixels around the cursor, a color preview, and the RGB and HEX values
  • Use the keyboard shortcuts to copy the color:
    • CTRL + Left Click → copies the color in HEX format (e.g. #FF5733)
    • ALT + Left Click → copies the color in RGB format (e.g. rgb(255, 87, 51))
    • ESC → closes the app

Libraries used:



Notes:


  • The clipboard content persists after the app closes (uses Windows clip.exe internally)
  • The NativeHook is properly unregistered before the app exits to avoid dangling hooks
  • Works on Windows

1774356719599.png

Here is the source code:

Main:
#Region Project Attributes
    #MainFormWidth: 600
    #MainFormHeight: 600
#End Region

Sub Process_Globals
    Private fx As JFX
    Private xui As XUI
   
    Private MainForm As Form
    Private OverlayForm As Form
   
    Private Robot As JavaObject
    Private Timer1 As Timer
   
    Private pnlColor As B4XView
    Private lblColor As B4XView
    Private pnlPreview As B4XView
    Private pnlZoom As B4XView
    Private ZoomCanvas As JavaObject
   
    Private LastR, LastG, LastB As Int
   
    Private KEY_CTRL_PRESSED As Boolean = False
    Private KEY_ALT_PRESSED As Boolean = False
   
    Private NH As NativeHook
End Sub

Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
   
    ' ===== NATIVE HOOK =====
    NH.Initialize("NH", Me)
    NH.startNativeKeyListener
    NH.startNativeMouseInputListener
    Log("NH iniciado")
   
    ' Torna invisível antes de mostrar
    MainForm.WindowLeft = -9999
    MainForm.WindowTop = -9999
    MainForm.Show

    ' Pega a window uma única vez e usa para tudo
    Dim stageMain As JavaObject = MainForm.RootPane
    Dim sceneMain As JavaObject = stageMain.RunMethod("getScene", Null)
    Dim windowMain As JavaObject = sceneMain.RunMethod("getWindow", Null)
    windowMain.RunMethod("setOpacity", Array(0.0))

    ' Registra evento de fechamento
    Dim closeHandler As Object = windowMain.CreateEventFromUI("javafx.event.EventHandler", "AppClosing", Null)
    windowMain.RunMethod("setOnCloseRequest", Array(closeHandler))

    ' ===== OVERLAY =====
    OverlayForm.Initialize("OverlayForm", 210, 265)
    OverlayForm.SetFormStyle("UNDECORATED")
    OverlayForm.BackColor = fx.Colors.Transparent
    OverlayForm.AlwaysOnTop = True
   
    pnlColor = xui.CreatePanel("pnlColor")
    pnlColor.SetColorAndBorder(xui.Color_RGB(245,245,245), 1, xui.Color_RGB(80,80,80), 6)
   
    pnlPreview = xui.CreatePanel("")
    pnlPreview.SetColorAndBorder(xui.Color_White, 1, xui.Color_RGB(80,80,80), 4)
    pnlColor.AddView(pnlPreview, 5, 5, 40, 40)
   
    lblColor = XUIViewsUtils.CreateLabel
    lblColor.SetTextAlignment("CENTER", "CENTER")
    lblColor.Font = xui.CreateDefaultFont(13)
    lblColor.Color = xui.Color_Transparent
    lblColor.TextColor = xui.Color_RGB(30,30,30)
    pnlColor.AddView(lblColor, 52, 0, 153, 50)
   
    pnlZoom = xui.CreatePanel("")
    pnlZoom.SetColorAndBorder(xui.Color_RGB(30,30,30), 1, xui.Color_RGB(80,80,80), 4)
    pnlColor.AddView(pnlZoom, 5, 58, 200, 200)
   
    ZoomCanvas.InitializeNewInstance("javafx.scene.canvas.Canvas", Array(200.0, 200.0))
    Dim zoomNode As JavaObject = pnlZoom
    Dim children As JavaObject = zoomNode.RunMethod("getChildren", Null)
    children.RunMethod("add", Array(ZoomCanvas))
   
    OverlayForm.RootPane.AddNode(pnlColor, 0, 0, 210, 265)
    OverlayForm.Show
   
    ' ===== ROBOT =====
    Robot.InitializeNewInstance("java.awt.Robot", Null)
   
    ' ===== TIMER =====
    Timer1.Initialize("Timer1", 50)
    Timer1.Enabled = True
End Sub

Sub NH_NativeKeyPressed(nke As NativeKeyEvent) As Boolean
    Select nke.keyText
        Case "Escape"
            CallSubDelayed(Me, "FecharApp")
        Case "Ctrl"
            KEY_CTRL_PRESSED = True
        Case "Alt"
            KEY_ALT_PRESSED = True
    End Select
    Return False
End Sub

Sub NH_NativeKeyReleased(nke As NativeKeyEvent) As Boolean
    Select nke.keyText
        Case "Ctrl"
            KEY_CTRL_PRESSED = False
        Case "Alt"
            KEY_ALT_PRESSED = False
    End Select
    Return False
End Sub

Sub NH_NativeMousePressed(nme As NativeMouseEvent) As Boolean
    If nme.MouseButton = 1 Then
        If KEY_CTRL_PRESSED Then
            CallSubDelayed(Me, "CopiarCorHEX")
        Else If KEY_ALT_PRESSED Then
            CallSubDelayed(Me, "CopiarCorRGB")
        End If
    End If
    Return False
End Sub

Sub CopiarCorHEX
    CopiarCor("HEX")
End Sub

Sub CopiarCorRGB
    CopiarCor("RGB")
End Sub

Sub AppClosing_Event(MethodName As String, Args() As Object) As Object
    NH.unregisterNativeHook
    Return Null
End Sub

Sub NH_Unregistered
    Log("Hook desregistrado corretamente")
    ExitApplication
End Sub

Sub FecharApp
    NH.unregisterNativeHook
End Sub

Sub Timer1_Tick
    ' ===== POSIÇÃO DO MOUSE =====
    Dim MouseInfo As JavaObject
    MouseInfo.InitializeStatic("java.awt.MouseInfo")
    Dim pointerInfo As JavaObject = MouseInfo.RunMethod("getPointerInfo", Null)
    Dim p As JavaObject = pointerInfo.RunMethod("getLocation", Null)
    Dim x As Int = p.RunMethod("getX", Null)
    Dim y As Int = p.RunMethod("getY", Null)
   
    ' ===== COR DO PIXEL CENTRAL =====
    Dim colorObj As JavaObject = Robot.RunMethod("getPixelColor", Array(x, y))
    Dim color As Int = colorObj.RunMethod("getRGB", Null)
   
    LastR = Bit.And(Bit.ShiftRight(color, 16), 0xFF)
    LastG = Bit.And(Bit.ShiftRight(color, 8), 0xFF)
    LastB = Bit.And(color, 0xFF)
   
    pnlPreview.Color = xui.Color_RGB(LastR, LastG, LastB)
   
    Dim hexColor As String = $"#${ToHex(LastR)}${ToHex(LastG)}${ToHex(LastB)}"$
    lblColor.Text = $"rgb(${LastR}, ${LastG}, ${LastB})${Chr(10)}${hexColor}"$
   
    ' ===== ZOOM =====
    Dim zoomSize As Int = 9
    Dim halfZoom As Int = zoomSize / 2
    Dim canvasW As Double = 200.0
    Dim canvasH As Double = 200.0
    Dim cellW As Double = canvasW / zoomSize
    Dim cellH As Double = canvasH / zoomSize
   
    Dim rect As JavaObject
    rect.InitializeNewInstance("java.awt.Rectangle", Array(x - halfZoom, y - halfZoom, zoomSize, zoomSize))
    Dim screenshot As JavaObject = Robot.RunMethod("createScreenCapture", Array(rect))
   
    Dim gc As JavaObject = ZoomCanvas.RunMethod("getGraphicsContext2D", Null)
   
    Dim fxColor As JavaObject
    fxColor.InitializeStatic("javafx.scene.paint.Color")
    Dim bgColor As JavaObject = fxColor.RunMethod("rgb", Array(30, 30, 30))
    gc.RunMethod("setFill", Array(bgColor))
    gc.RunMethod("fillRect", Array(0.0, 0.0, canvasW, canvasH))
   
    Dim row As Int
    Dim col As Int
    For row = 0 To zoomSize - 1
        For col = 0 To zoomSize - 1
            Dim pc As Int = screenshot.RunMethod("getRGB", Array(col, row))
            Dim pr As Int = Bit.And(Bit.ShiftRight(pc, 16), 0xFF)
            Dim pg As Int = Bit.And(Bit.ShiftRight(pc, 8), 0xFF)
            Dim pb As Int = Bit.And(pc, 0xFF)
           
            Dim fillColor As JavaObject = fxColor.RunMethod("rgb", Array(pr, pg, pb))
            gc.RunMethod("setFill", Array(fillColor))
           
            Dim colD As Double = col
            Dim rowD As Double = row
            gc.RunMethod("fillRect", Array(colD * cellW, rowD * cellH, cellW, cellH))
           
            If row = halfZoom And col = halfZoom Then
                Dim crossColor As JavaObject = fxColor.RunMethod("rgb", Array(255, 255, 255))
                gc.RunMethod("setStroke", Array(crossColor))
                gc.RunMethod("setLineWidth", Array(1.5))
                gc.RunMethod("strokeRect", Array(colD * cellW, rowD * cellH, cellW, cellH))
            End If
        Next
    Next
   
    ' ===== OVERLAY SEGUE O MOUSE =====
    OverlayForm.WindowLeft = x + 20
    OverlayForm.WindowTop = y + 20
End Sub

Sub CopiarCor(modo As String)
    Dim hexColor As String = $"#${ToHex(LastR)}${ToHex(LastG)}${ToHex(LastB)}"$
    Dim rgbText As String = $"rgb(${LastR}, ${LastG}, ${LastB})"$
   
    Dim textoCopiar As String
    If modo = "HEX" Then
        textoCopiar = hexColor
    Else
        textoCopiar = rgbText
    End If
   
    ' Salva em arquivo temp e usa clip.exe do Windows
    File.WriteString(File.DirApp, "colorpicker_clip.txt", textoCopiar)
    Dim tempFile As String = File.Combine(File.DirApp, "colorpicker_clip.txt")
   
    Dim cmd As List
    cmd.Initialize
    cmd.Add("cmd")
    cmd.Add("/c")
    cmd.Add("type " & tempFile & " | clip")
   
    Dim pb As JavaObject
    pb.InitializeNewInstance("java.lang.ProcessBuilder", Array(cmd))
    Dim proc As JavaObject = pb.RunMethod("start", Null)
    proc.RunMethod("waitFor", Null)
   
    Log("Copiado: " & textoCopiar)
   
    FecharApp
End Sub

Sub ToHex(value As Int) As String
    Dim hex As String = "0123456789ABCDEF"
    Return hex.CharAt(Bit.ShiftRight(value, 4)) & hex.CharAt(Bit.And(value, 15))
End Sub


Hope it helps someone! 😊
 

Attachments

  • colorpicker.zip
    4.3 KB · Views: 45
Last edited:
Top