mirror of
https://github.com/obsqrbtz/clrsync.git
synced 2026-04-08 20:19:04 +03:00
feat: added eyedropper
This commit is contained in:
@@ -1,13 +1,12 @@
|
|||||||
#ifndef CLRSYNC_CORE_VERSION_HPP
|
#ifndef CLRSYNC_CORE_VERSION_HPP
|
||||||
#define CLRSYNC_CORE_VERSION_HPP
|
#define CLRSYNC_CORE_VERSION_HPP
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace clrsync::core
|
namespace clrsync::core
|
||||||
{
|
{
|
||||||
|
|
||||||
const std::string GIT_SEMVER = "0.1.4+git.g89888ad";
|
const std::string GIT_SEMVER = "0.1.4+git.ge6bac8e";
|
||||||
|
|
||||||
const std::string version_string();
|
const std::string version_string();
|
||||||
} // namespace clrsync::core
|
} // namespace clrsync::core
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#ifndef CLRSYNC_CORE_VERSION_HPP
|
#ifndef CLRSYNC_CORE_VERSION_HPP
|
||||||
#define CLRSYNC_CORE_VERSION_HPP
|
#define CLRSYNC_CORE_VERSION_HPP
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace clrsync::core
|
namespace clrsync::core
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ set(GUI_SOURCES
|
|||||||
main.cpp
|
main.cpp
|
||||||
color_scheme_editor.cpp
|
color_scheme_editor.cpp
|
||||||
color_table_renderer.cpp
|
color_table_renderer.cpp
|
||||||
|
screen_eyedropper.cpp
|
||||||
preview_renderer.cpp
|
preview_renderer.cpp
|
||||||
theme_applier.cpp
|
theme_applier.cpp
|
||||||
template_editor.cpp
|
template_editor.cpp
|
||||||
@@ -27,8 +28,18 @@ if(WIN32)
|
|||||||
clrsync_core
|
clrsync_core
|
||||||
glfw
|
glfw
|
||||||
imgui
|
imgui
|
||||||
|
user32
|
||||||
|
gdi32
|
||||||
OpenGL::GL
|
OpenGL::GL
|
||||||
)
|
)
|
||||||
|
elseif(APPLE)
|
||||||
|
target_link_libraries(clrsync_gui PRIVATE
|
||||||
|
clrsync_core
|
||||||
|
glfw
|
||||||
|
imgui
|
||||||
|
OpenGL::GL
|
||||||
|
"-framework ApplicationServices"
|
||||||
|
)
|
||||||
else()
|
else()
|
||||||
target_link_libraries(clrsync_gui PRIVATE
|
target_link_libraries(clrsync_gui PRIVATE
|
||||||
clrsync_core
|
clrsync_core
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "color_table_renderer.hpp"
|
#include "color_table_renderer.hpp"
|
||||||
|
#include "screen_eyedropper.hpp"
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -9,10 +10,19 @@ void color_table_renderer::render_color_row(const std::string &name,
|
|||||||
{
|
{
|
||||||
const clrsync::core::color &col = current.get_color(name);
|
const clrsync::core::color &col = current.get_color(name);
|
||||||
|
|
||||||
|
const bool is_picking = m_screen_pick.active && m_screen_pick.key == name;
|
||||||
|
|
||||||
ImGui::TableNextRow();
|
ImGui::TableNextRow();
|
||||||
|
|
||||||
ImGui::TableSetColumnIndex(0);
|
ImGui::TableSetColumnIndex(0);
|
||||||
ImGui::TextUnformatted(name.c_str());
|
const float key_col_width = ImGui::GetContentRegionAvail().x;
|
||||||
|
const bool copied = ImGui::Selectable(name.c_str(), false, 0, ImVec2(key_col_width, 0.0f));
|
||||||
|
if (ImGui::IsItemHovered())
|
||||||
|
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
|
||||||
|
if (copied)
|
||||||
|
{
|
||||||
|
ImGui::SetClipboardText(name.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::TableSetColumnIndex(1);
|
ImGui::TableSetColumnIndex(1);
|
||||||
{
|
{
|
||||||
@@ -44,11 +54,35 @@ void color_table_renderer::render_color_row(const std::string &name,
|
|||||||
float c[4] = {((col.hex() >> 24) & 0xFF) / 255.0f, ((col.hex() >> 16) & 0xFF) / 255.0f,
|
float c[4] = {((col.hex() >> 24) & 0xFF) / 255.0f, ((col.hex() >> 16) & 0xFF) / 255.0f,
|
||||||
((col.hex() >> 8) & 0xFF) / 255.0f, (col.hex() & 0xFF) / 255.0f};
|
((col.hex() >> 8) & 0xFF) / 255.0f, (col.hex() & 0xFF) / 255.0f};
|
||||||
|
|
||||||
|
if (is_picking)
|
||||||
|
{
|
||||||
|
float sampled_rgb[3];
|
||||||
|
if (clrsync::gui::sample_screen_rgb(sampled_rgb))
|
||||||
|
{
|
||||||
|
m_screen_pick.rgba[0] = sampled_rgb[0];
|
||||||
|
m_screen_pick.rgba[1] = sampled_rgb[1];
|
||||||
|
m_screen_pick.rgba[2] = sampled_rgb[2];
|
||||||
|
m_screen_pick.rgba[3] = m_screen_pick.alpha;
|
||||||
|
m_screen_pick.has_sample = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_screen_pick.has_sample)
|
||||||
|
{
|
||||||
|
c[0] = m_screen_pick.rgba[0];
|
||||||
|
c[1] = m_screen_pick.rgba[1];
|
||||||
|
c[2] = m_screen_pick.rgba[2];
|
||||||
|
c[3] = m_screen_pick.rgba[3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::SetNextItemWidth(-FLT_MIN);
|
ImGui::SetNextItemWidth(-FLT_MIN);
|
||||||
if (ImGui::ColorEdit4(("##color_" + name).c_str(), c,
|
if (ImGui::ColorEdit4(("##color_" + name).c_str(), c,
|
||||||
ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel |
|
ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel |
|
||||||
ImGuiColorEditFlags_AlphaBar))
|
ImGuiColorEditFlags_AlphaBar))
|
||||||
{
|
{
|
||||||
|
if (is_picking)
|
||||||
|
m_screen_pick.active = false;
|
||||||
|
|
||||||
uint32_t r = (uint32_t)(c[0] * 255.0f);
|
uint32_t r = (uint32_t)(c[0] * 255.0f);
|
||||||
uint32_t g = (uint32_t)(c[1] * 255.0f);
|
uint32_t g = (uint32_t)(c[1] * 255.0f);
|
||||||
uint32_t b = (uint32_t)(c[2] * 255.0f);
|
uint32_t b = (uint32_t)(c[2] * 255.0f);
|
||||||
@@ -60,6 +94,51 @@ void color_table_renderer::render_color_row(const std::string &name,
|
|||||||
on_changed();
|
on_changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (!is_picking)
|
||||||
|
{
|
||||||
|
if (ImGui::Button("Pick"))
|
||||||
|
{
|
||||||
|
m_screen_pick.active = true;
|
||||||
|
m_screen_pick.key = name;
|
||||||
|
m_screen_pick.alpha = c[3];
|
||||||
|
m_screen_pick.has_sample = false;
|
||||||
|
}
|
||||||
|
if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort))
|
||||||
|
ImGui::SetTooltip("Pick a color from anywhere on the screen");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (ImGui::Button("Cancel"))
|
||||||
|
{
|
||||||
|
m_screen_pick.active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool confirm =
|
||||||
|
ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter) ||
|
||||||
|
ImGui::IsKeyPressed(ImGuiKey_Space);
|
||||||
|
const bool cancel = ImGui::IsKeyPressed(ImGuiKey_Escape);
|
||||||
|
|
||||||
|
if (cancel)
|
||||||
|
{
|
||||||
|
m_screen_pick.active = false;
|
||||||
|
}
|
||||||
|
else if (confirm && m_screen_pick.has_sample)
|
||||||
|
{
|
||||||
|
uint32_t r = (uint32_t)(c[0] * 255.0f);
|
||||||
|
uint32_t g = (uint32_t)(c[1] * 255.0f);
|
||||||
|
uint32_t b = (uint32_t)(c[2] * 255.0f);
|
||||||
|
uint32_t a = (uint32_t)(c[3] * 255.0f);
|
||||||
|
uint32_t hex = (r << 24) | (g << 16) | (b << 8) | a;
|
||||||
|
|
||||||
|
controller.set_color(name, clrsync::core::color(hex));
|
||||||
|
if (on_changed)
|
||||||
|
on_changed();
|
||||||
|
|
||||||
|
m_screen_pick.active = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,6 +155,13 @@ void color_table_renderer::render(const clrsync::core::palette& current,
|
|||||||
ImGui::Text("Color Variables");
|
ImGui::Text("Color Variables");
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
|
||||||
|
if (m_screen_pick.active)
|
||||||
|
{
|
||||||
|
ImGui::TextUnformatted(
|
||||||
|
"Screen picker active: move cursor anywhere, press Enter/Space to pick, Esc to cancel");
|
||||||
|
ImGui::Spacing();
|
||||||
|
}
|
||||||
|
|
||||||
auto draw_table = [&](const char *title, const std::vector<const char *> &keys) {
|
auto draw_table = [&](const char *title, const std::vector<const char *> &keys) {
|
||||||
ImGui::TextUnformatted(title);
|
ImGui::TextUnformatted(title);
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "core/palette/palette.hpp"
|
#include "core/palette/palette.hpp"
|
||||||
#include "palette_controller.hpp"
|
#include "palette_controller.hpp"
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
class color_table_renderer
|
class color_table_renderer
|
||||||
{
|
{
|
||||||
@@ -19,6 +20,17 @@ private:
|
|||||||
const clrsync::core::palette& palette,
|
const clrsync::core::palette& palette,
|
||||||
palette_controller& controller,
|
palette_controller& controller,
|
||||||
const OnColorChangedCallback& on_changed);
|
const OnColorChangedCallback& on_changed);
|
||||||
|
|
||||||
|
struct screen_pick_state
|
||||||
|
{
|
||||||
|
bool active{false};
|
||||||
|
std::string key;
|
||||||
|
float rgba[4]{0.0f, 0.0f, 0.0f, 1.0f};
|
||||||
|
float alpha{1.0f};
|
||||||
|
bool has_sample{false};
|
||||||
|
};
|
||||||
|
|
||||||
|
screen_pick_state m_screen_pick;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CLRSYNC_GUI_COLOR_TABLE_RENDERER_HPP
|
#endif // CLRSYNC_GUI_COLOR_TABLE_RENDERER_HPP
|
||||||
|
|||||||
158
src/gui/screen_eyedropper.cpp
Normal file
158
src/gui/screen_eyedropper.cpp
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
#include "screen_eyedropper.hpp"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
#include <ApplicationServices/ApplicationServices.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__linux__)
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <X11/Xutil.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#ifndef NOMINMAX
|
||||||
|
#define NOMINMAX
|
||||||
|
#endif
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace clrsync::gui
|
||||||
|
{
|
||||||
|
bool sample_screen_rgb(float out_rgb[3])
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
POINT pt;
|
||||||
|
if (!GetCursorPos(&pt))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
HDC hdc = GetDC(nullptr);
|
||||||
|
if (!hdc)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const COLORREF color = GetPixel(hdc, pt.x, pt.y);
|
||||||
|
ReleaseDC(nullptr, hdc);
|
||||||
|
|
||||||
|
if (color == CLR_INVALID)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const uint8_t r = GetRValue(color);
|
||||||
|
const uint8_t g = GetGValue(color);
|
||||||
|
const uint8_t b = GetBValue(color);
|
||||||
|
|
||||||
|
out_rgb[0] = r / 255.0f;
|
||||||
|
out_rgb[1] = g / 255.0f;
|
||||||
|
out_rgb[2] = b / 255.0f;
|
||||||
|
return true;
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
CGEventRef event = CGEventCreate(nullptr);
|
||||||
|
if (!event)
|
||||||
|
return false;
|
||||||
|
const CGPoint pt = CGEventGetLocation(event);
|
||||||
|
CFRelease(event);
|
||||||
|
|
||||||
|
const CGRect rect = CGRectMake(pt.x, pt.y, 1, 1);
|
||||||
|
CGImageRef image =
|
||||||
|
CGWindowListCreateImage(rect, kCGWindowListOptionOnScreenOnly, kCGNullWindowID,
|
||||||
|
kCGWindowImageDefault);
|
||||||
|
if (!image)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
uint8_t pixel[4] = {0, 0, 0, 255};
|
||||||
|
CGColorSpaceRef color_space = CGColorSpaceCreateDeviceRGB();
|
||||||
|
if (!color_space)
|
||||||
|
{
|
||||||
|
CGImageRelease(image);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CGContextRef ctx = CGBitmapContextCreate(pixel, 1, 1, 8, 4, color_space,
|
||||||
|
kCGImageAlphaPremultipliedLast |
|
||||||
|
kCGBitmapByteOrder32Big);
|
||||||
|
CGColorSpaceRelease(color_space);
|
||||||
|
if (!ctx)
|
||||||
|
{
|
||||||
|
CGImageRelease(image);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CGContextDrawImage(ctx, CGRectMake(0, 0, 1, 1), image);
|
||||||
|
CGContextRelease(ctx);
|
||||||
|
CGImageRelease(image);
|
||||||
|
|
||||||
|
out_rgb[0] = pixel[0] / 255.0f;
|
||||||
|
out_rgb[1] = pixel[1] / 255.0f;
|
||||||
|
out_rgb[2] = pixel[2] / 255.0f;
|
||||||
|
return true;
|
||||||
|
#elif defined(__linux__)
|
||||||
|
Display *display = XOpenDisplay(nullptr);
|
||||||
|
if (!display)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const Window root = DefaultRootWindow(display);
|
||||||
|
Window root_ret, child_ret;
|
||||||
|
int root_x = 0, root_y = 0;
|
||||||
|
int win_x = 0, win_y = 0;
|
||||||
|
unsigned int mask = 0;
|
||||||
|
|
||||||
|
if (!XQueryPointer(display, root, &root_ret, &child_ret, &root_x, &root_y, &win_x, &win_y,
|
||||||
|
&mask))
|
||||||
|
{
|
||||||
|
XCloseDisplay(display);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
XImage *img = XGetImage(display, root, root_x, root_y, 1, 1, AllPlanes, ZPixmap);
|
||||||
|
if (!img)
|
||||||
|
{
|
||||||
|
XCloseDisplay(display);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const unsigned long pixel = XGetPixel(img, 0, 0);
|
||||||
|
|
||||||
|
auto channel_from_mask = [](unsigned long px, unsigned long mask_val) -> uint8_t {
|
||||||
|
if (mask_val == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
unsigned int shift = 0;
|
||||||
|
while ((mask_val & 1UL) == 0)
|
||||||
|
{
|
||||||
|
mask_val >>= 1;
|
||||||
|
shift++;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int bits = 0;
|
||||||
|
while (mask_val & 1UL)
|
||||||
|
{
|
||||||
|
mask_val >>= 1;
|
||||||
|
bits++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bits == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
const unsigned long max_val = (bits >= 32) ? 0xFFFFFFFFUL : ((1UL << bits) - 1UL);
|
||||||
|
const unsigned long val = (px >> shift) & max_val;
|
||||||
|
const unsigned long scaled = (val * 255UL) / max_val;
|
||||||
|
return static_cast<uint8_t>(scaled & 0xFF);
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint8_t r = channel_from_mask(pixel, img->red_mask);
|
||||||
|
const uint8_t g = channel_from_mask(pixel, img->green_mask);
|
||||||
|
const uint8_t b = channel_from_mask(pixel, img->blue_mask);
|
||||||
|
|
||||||
|
XDestroyImage(img);
|
||||||
|
XCloseDisplay(display);
|
||||||
|
|
||||||
|
out_rgb[0] = r / 255.0f;
|
||||||
|
out_rgb[1] = g / 255.0f;
|
||||||
|
out_rgb[2] = b / 255.0f;
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
(void)out_rgb;
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
} // namespace clrsync::gui
|
||||||
9
src/gui/screen_eyedropper.hpp
Normal file
9
src/gui/screen_eyedropper.hpp
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#ifndef CLRSYNC_GUI_SCREEN_EYEDROPPER_HPP
|
||||||
|
#define CLRSYNC_GUI_SCREEN_EYEDROPPER_HPP
|
||||||
|
|
||||||
|
namespace clrsync::gui
|
||||||
|
{
|
||||||
|
bool sample_screen_rgb(float out_rgb[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CLRSYNC_GUI_SCREEN_EYEDROPPER_HPP
|
||||||
Reference in New Issue
Block a user