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.
How it works:
Libraries used:
Notes:
Here is the source code:
Hope it helps someone!
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.
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:
- XUI (included with B4J)
- jAWTRobot - for capturing screen pixels: https://www.b4x.com/android/forum/threads/jawtrobot-invoke-keyboard-and-mouse-events-etc.55832/
- jNativeHookB4J - for global keyboard and mouse input (works even when the app is not in focus): https://www.b4x.com/android/forum/threads/jnativehookb4j-for-intercepting-system-input-events.55826/
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
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
Last edited: