Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
AmjadHD opened this issue Sep 2, 2020 · 3 comments

Comments

@AmjadHD
Copy link
Contributor

AmjadHD commented Sep 2, 2020

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.

@rwols
Copy link
Member

rwols commented Nov 8, 2020

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)

@jwortmann
Copy link
Member

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")

@rwols
Copy link
Member

rwols commented Nov 10, 2020

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants