LSP icon indicating copy to clipboard operation
LSP copied to clipboard

Investigate the possibility to trigger a color picker on clicking color boxes

Open AmjadHD opened this issue 4 years ago • 3 comments

It would be neat to be able to trigger a color picker by clicking a color box. This probably means LSP should ship with or download platform-dependent color picker executables, and add anchor tags to all color boxes phantoms.

AmjadHD avatar Sep 02 '20 10:09 AmjadHD

I was looking into this. A popular color picker on packagecontrol.io is this one: https://github.com/weslly/ColorPicker

That package puts compiled binaries in the git repo. The source code is seemingly:

https://github.com/jnordberg/color-pick/blob/master/colorpick.m (macOS)

I think this can be done a lot easier for macOS at least... here's a plugin that does about the same thing

import sublime
import subprocess
import json


__script = '''
function run(argv) {{
    var app = Application.currentApplication();
    app.includeStandardAdditions = true;
    var color = app.chooseColor({{defaultColor: [{red}, {green}, {blue}]}});
    console.log(JSON.stringify(color));
}}
'''


def plugin_loaded():
    script = __script.format(red=0.5, green=0.2, blue=0.1)
    args = ("/usr/bin/osascript", "-l", "JavaScript", "-e", script)
    output = subprocess.check_output(args, stderr=subprocess.STDOUT)
    color = json.loads(output.decode('ascii').strip())
    print("color:", color)

rwols avatar Nov 08 '20 22:11 rwols

Here is the essential code for Windows, extracted from that package. It uses the native color picker from the win32 API (no additional binary required) and doesn't support an alpha channel for colors. ST becomes unresponsive as long as the color picker dialog is open, which probably could be avoided by using a separate thread for the color picker, but more code would be required then before inserting the color result into the buffer if the buffer was changed in the meantime. So it's a poor mans "modal dialog" solution for now ;)

import sublime
import sublime_plugin
import ctypes


CC_SOLIDCOLOR = 0x80
CC_RGBINIT = 0x01
CC_FULLOPEN = 0x02

red = 0.5
green = 0.2
blue = 0.1
default_color = (round(255*blue) << 16) | (round(255*green) << 8) | round(255*red)

class CHOOSECOLOR(ctypes.Structure):
    _fields_ = [
        ("lStructSize", ctypes.c_uint32),
        ("hwndOwner", ctypes.c_void_p),
        ("hInstance", ctypes.c_void_p),
        ("rgbResult", ctypes.c_uint32),
        ("lpCustColors", ctypes.POINTER(ctypes.c_uint32)),
        ("Flags", ctypes.c_uint32),
        ("lCustData", ctypes.c_void_p),
        ("lpfnHook", ctypes.c_void_p),
        ("lpTemplateName", ctypes.c_wchar_p)]

def bgr2hexcolor(bgr):
        # 0x00BBGGRR
        byte_table = list(["{0:02X}".format(b) for b in range(256)])
        b = byte_table[(bgr >> 16) & 0xff]
        g = byte_table[(bgr >> 8) & 0xff]
        r = byte_table[(bgr) & 0xff]
        return "#" + (r + g + b).lower()


def plugin_loaded():
    ChooseColorW = ctypes.windll.Comdlg32.ChooseColorW
    ChooseColorW.argtypes = [ctypes.POINTER(CHOOSECOLOR)]
    ChooseColorW.restype = ctypes.c_int32

    cc = CHOOSECOLOR()
    ctypes.memset(ctypes.byref(cc), 0, ctypes.sizeof(cc))
    cc.lStructSize = ctypes.sizeof(cc)
    cc.hwndOwner = None
    CustomColors = ctypes.c_uint32 * 16
    cc.lpCustColors = CustomColors() # uses 0 (black) for all 16 predefined custom colors
    cc.rgbResult = ctypes.c_uint32(default_color)
    cc.Flags = CC_SOLIDCOLOR | CC_FULLOPEN | CC_RGBINIT

    # ST window will become unresponsive until color picker dialog is closed
    result = ChooseColorW(ctypes.byref(cc))

    if result == 1: # user clicked OK
        print(bgr2hexcolor(cc.rgbResult))
    elif result == 0:
        print("User clicked Cancel or closed the dialog")

jwortmann avatar Nov 09 '20 15:11 jwortmann

Relevant request: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_colorPresentation

rwols avatar Nov 10 '20 19:11 rwols