• Tutorials
  • How to customize a ColorPickerButton (to make colors selectable with a keyboard or controller)

This is something easy to implement, but kind of convoluted to figure out. I went digging because the ColorPicker's default focus_mode settings don't allow it to behave the way I'd expect for a videogame.

First, I learned that the ColorPickerButton instances a whole tree of nodes as if it was a scene. In order to change anything, you have to find the right child in each part of the tree -- I just did that using the "Remote" function in the editor's Scene menu, so if you're reading this, you don't have to. :)

It works well to connect the button's picker_created() signal to a script. It is emitted just one time when the button is first pressed, and it keeps the customized ColorPicker after that. Here is a GDScript template to access the relevant nodes you might like to customize (I've used quote blocks because the "code" markup stubbornly refuses to cooperate; there's only one level of indentation anyway):

func _on_ColorPickerButton_picker_created():
	var picker = $ColorPickerButton.get_child(0).get_child(0)
	picker.get_child(0).get_child(0)  # Visual saturation/value selector box
	picker.get_child(0).get_child(1)  # Visual hue slider
	
	picker.get_child(1).get_child(0)  # Selected color
	picker.get_child(1).get_child(1)  # Eyedropper
	
	picker.get_child(3)  # Divider below selected color (if hiding all above)
	
	picker.get_child(4).get_child(0).get_child(1)  # Red/Hue slider
	picker.get_child(4).get_child(0).get_child(2)  # Red/Hue spinbox
	picker.get_child(4).get_child(1).get_child(1)  # Green/Saturation slider
	picker.get_child(4).get_child(1).get_child(2)  # Green/Saturation spinbox
	picker.get_child(4).get_child(2).get_child(1)  # Blue/Value slider
	picker.get_child(4).get_child(2).get_child(2)  # Blue/Value spinbox
	picker.get_child(4).get_child(3)  # Alpha row (hidden by default)
	
	picker.get_child(4).get_child(4).get_child(0)  # HSV toggle
	picker.get_child(4).get_child(4).get_child(1)  # "Raw" toggle
	picker.get_child(4).get_child(4).get_child(2)  # Hex '#' label (why focus_mode == 2??)
	picker.get_child(4).get_child(4).get_child(3)  # Hex LineEdit
	
	picker.get_child(5)  # Divider to presets (if hiding the preset function)
	picker.get_child(6)  # Preset colors (all of them)
	picker.get_child(7)  # Add preset button

And here is an example that enables the player to select a color without having to reach for a mouse or keyboard, by switching focus_mode settings. It is more like selecting a color in most videogames I play that have RGB/HSV selectors:

func _on_ColorPickerButton_picker_created():
   	var picker = $ColorPickerButton.get_child(0).get_child(0)
   	picker.get_child(1).get_child(1).set_focus_mode(1) # Eye dropper is a mouse tool
   	
   	# Allow sliders to acquire focus and reserve the SpinBox's LineEdit (child 0) for a mouse
   	picker.get_child(4).get_child(0).get_child(1).set_focus_mode(2) # Red/Hue slider
   	picker.get_child(4).get_child(0).get_child(2).get_child(0).set_focus_mode(1) # Red/Hue spinbox
   	picker.get_child(4).get_child(1).get_child(1).set_focus_mode(2) # Green/Sat slider
   	picker.get_child(4).get_child(1).get_child(2).get_child(0).set_focus_mode(1) # Green/Sat spinbox
   	picker.get_child(4).get_child(2).get_child(1).set_focus_mode(2) # Blue/Value slider
   	picker.get_child(4).get_child(2).get_child(2).get_child(0).set_focus_mode(1) # Blue/Value spinbox
   	
   	picker.get_child(4).get_child(4).get_child(1).hide() # Raw toggle; not a conventional feature
   	picker.get_child(4).get_child(4).get_child(2).set_focus_mode(0) # Skip the hex '#' label!
   	picker.get_child(4).get_child(4).get_child(3).set_focus_mode(1) # Hex LineEdit

Just a heads up, while I was editing your post to fix code formatting(you had used quotation instead) I noticed that the second code block had 2 levels of indentation.

@Megalomaniak -- Thanks for fixing it. :) I know I used quotes; like I said, the code formatting would not work at all. It completely ignored the markup. I tried five times with a blank, fresh thread creation each time, but it would only format portions of the post; the same portions every time, even if I did not apply the code formatting (which is weird).

The two levels of indentation would be an artifact of trying and failing to get the code to show properly.

So as a heads up in return, the code formatting has not been cooperative every time I've used it here. If it could make a difference, I use Waterfox for a browser; currently version G3.1.1.

Easiest way to add a code block is to simply use ~~~ on previous and following lines to encapsulate it.

@Megalomaniak -- Cool, I'll try that. The grave accent is what wasn't working.

func express_gratitude(name):
	if name == "Megalomaniak":
		print(str("Thanks for the tip, ", name, "."))

That's better. :+1:

4 years later

To anyone hitting this in Godot 4 (4.4 for me, currently), there's a much simpler way to toggle the items off/on without having to dig into the children. You can use the same signal mentioned above, but now there are properties avaialable:

@onready var picker_button: ColorPickerButton = $ColorPickerButton

func _on_color_picker_button_picker_created() -> void:
	var picker: ColorPicker = picker_button.get_picker()
	picker.presets_visible = false
	picker.color_modes_visible = false
	picker.edit_alpha = false
	picker.sliders_visible = false
	picker.picker_shape = ColorPicker.SHAPE_NONE
	picker.hex_visible = false
	picker.sampler_visible = false

Choose from that list what you want to disable and that's it!
Hope this helps!

Ps: if you copy and paste that code in exactly as is you will disable everything in the picker.