Merge branch 'Menotdan-color-picker'
This commit is contained in:
@@ -25,6 +25,7 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
{
|
||||
private const float ButtonsWidth = 60.0f;
|
||||
private const float PickerMargin = 6.0f;
|
||||
private const float EyedropperMargin = 8.0f;
|
||||
private const float RGBAMargin = 12.0f;
|
||||
private const float HSVMargin = 0.0f;
|
||||
private const float ChannelsMargin = 4.0f;
|
||||
@@ -34,6 +35,7 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
private Color _value;
|
||||
private bool _disableEvents;
|
||||
private bool _useDynamicEditing;
|
||||
private bool _activeEyedropper;
|
||||
private ColorValueBox.ColorPickerEvent _onChanged;
|
||||
private ColorValueBox.ColorPickerClosedEvent _onClosed;
|
||||
|
||||
@@ -48,6 +50,7 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
private TextBox _cHex;
|
||||
private Button _cCancel;
|
||||
private Button _cOK;
|
||||
private Button _cEyedropper;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the selected color.
|
||||
@@ -192,10 +195,44 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
};
|
||||
_cOK.Clicked += OnSubmit;
|
||||
|
||||
// Eyedropper button
|
||||
var style = Style.Current;
|
||||
_cEyedropper = new Button(_cOK.X - EyedropperMargin, _cHex.Bottom + PickerMargin)
|
||||
{
|
||||
TooltipText = "Eyedropper tool to pick a color directly from the screen",
|
||||
BackgroundBrush = new SpriteBrush(Editor.Instance.Icons.Search32),
|
||||
BackgroundColor = style.Foreground,
|
||||
BackgroundColorHighlighted = style.Foreground.RGBMultiplied(0.9f),
|
||||
BorderColor = Color.Transparent,
|
||||
BorderColorHighlighted = style.BorderSelected,
|
||||
Parent = this,
|
||||
};
|
||||
_cEyedropper.Clicked += OnEyedropStart;
|
||||
_cEyedropper.Height = (_cValue.Bottom - _cEyedropper.Y) * 0.5f;
|
||||
_cEyedropper.Width = _cEyedropper.Height;
|
||||
_cEyedropper.X -= _cEyedropper.Width;
|
||||
|
||||
// Set initial color
|
||||
SelectedColor = initialValue;
|
||||
}
|
||||
|
||||
private void OnColorPicked(Color32 colorPicked)
|
||||
{
|
||||
if (_activeEyedropper)
|
||||
{
|
||||
_activeEyedropper = false;
|
||||
SelectedColor = colorPicked;
|
||||
ScreenUtilities.PickColorDone -= OnColorPicked;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEyedropStart()
|
||||
{
|
||||
_activeEyedropper = true;
|
||||
ScreenUtilities.PickColor();
|
||||
ScreenUtilities.PickColorDone += OnColorPicked;
|
||||
}
|
||||
|
||||
private void OnRGBAChanged()
|
||||
{
|
||||
if (_disableEvents)
|
||||
@@ -221,6 +258,19 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
SelectedColor = color;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
base.Update(deltaTime);
|
||||
|
||||
// Update eye dropper tool
|
||||
if (_activeEyedropper)
|
||||
{
|
||||
Float2 mousePosition = Platform.MousePosition;
|
||||
SelectedColor = ScreenUtilities.GetColorAt(mousePosition);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
{
|
||||
@@ -274,6 +324,20 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
base.OnShow();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnKeyDown(KeyboardKeys key)
|
||||
{
|
||||
if (_activeEyedropper && key == KeyboardKeys.Escape)
|
||||
{
|
||||
// Cancel eye dropping
|
||||
_activeEyedropper = false;
|
||||
ScreenUtilities.PickColorDone -= OnColorPicked;
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnKeyDown(key);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnSubmit()
|
||||
{
|
||||
|
||||
@@ -57,6 +57,11 @@ namespace FlaxEditor.GUI.Input
|
||||
/// </summary>
|
||||
protected Color _value;
|
||||
|
||||
/// <summary>
|
||||
/// Enables live preview of the selected value from the picker. Otherwise will update the value only when user confirms it on dialog closing.
|
||||
/// </summary>
|
||||
public bool UseDynamicEditing = true;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when value gets changed.
|
||||
/// </summary>
|
||||
@@ -143,7 +148,7 @@ namespace FlaxEditor.GUI.Input
|
||||
base.OnSubmit();
|
||||
|
||||
// Show color picker dialog
|
||||
_currentDialog = ShowPickColorDialog?.Invoke(this, _value, OnColorChanged, OnPickerClosed);
|
||||
_currentDialog = ShowPickColorDialog?.Invoke(this, _value, OnColorChanged, OnPickerClosed, UseDynamicEditing);
|
||||
}
|
||||
|
||||
private void OnColorChanged(Color color, bool sliding)
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace FlaxEditor.Surface.Elements
|
||||
{
|
||||
ParentNode = parentNode;
|
||||
Archetype = archetype;
|
||||
|
||||
UseDynamicEditing = false;
|
||||
ParentNode.ValuesChanged += OnNodeValuesChanged;
|
||||
}
|
||||
|
||||
|
||||
130
Source/Editor/Utilities/ScreenUtilities.cpp
Normal file
130
Source/Editor/Utilities/ScreenUtilities.cpp
Normal file
@@ -0,0 +1,130 @@
|
||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "ScreenUtilities.h"
|
||||
#include "Engine/Core/Math/Vector2.h"
|
||||
#include "Engine/Core/Delegate.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
|
||||
Delegate<Color32> ScreenUtilities::PickColorDone;
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
#pragma comment(lib, "Gdi32.lib")
|
||||
|
||||
static HHOOK MouseCallbackHook;
|
||||
|
||||
LRESULT CALLBACK OnScreenUtilsMouseCallback(_In_ int nCode, _In_ WPARAM wParam, _In_ LPARAM lParam)
|
||||
{
|
||||
if (nCode >= 0 && wParam == WM_LBUTTONDOWN)
|
||||
{
|
||||
UnhookWindowsHookEx(MouseCallbackHook);
|
||||
|
||||
// Push event with the picked color
|
||||
const Float2 cursorPos = Platform::GetMousePosition();
|
||||
const Color32 colorPicked = ScreenUtilities::GetColorAt(cursorPos);
|
||||
ScreenUtilities::PickColorDone(colorPicked);
|
||||
return 1;
|
||||
}
|
||||
return CallNextHookEx(NULL, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
Color32 ScreenUtilities::GetColorAt(const Float2& pos)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
HDC deviceContext = GetDC(NULL);
|
||||
COLORREF color = GetPixel(deviceContext, (int)pos.X, (int)pos.Y);
|
||||
ReleaseDC(NULL, deviceContext);
|
||||
return Color32(GetRValue(color), GetGValue(color), GetBValue(color), 255);
|
||||
}
|
||||
|
||||
void ScreenUtilities::PickColor()
|
||||
{
|
||||
MouseCallbackHook = SetWindowsHookEx(WH_MOUSE_LL, OnScreenUtilsMouseCallback, NULL, NULL);
|
||||
if (MouseCallbackHook == NULL)
|
||||
{
|
||||
LOG(Warning, "Failed to set mouse hook.");
|
||||
LOG(Warning, "Error: {0}", GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
#elif PLATFORM_LINUX
|
||||
|
||||
#include "Engine/Platform/Linux/LinuxPlatform.h"
|
||||
#include "Engine/Platform/Linux/IncludeX11.h"
|
||||
|
||||
Color32 ScreenUtilities::GetColorAt(const Float2& pos)
|
||||
{
|
||||
X11::XColor color;
|
||||
|
||||
X11::Display* display = (X11::Display*) LinuxPlatform::GetXDisplay();
|
||||
int defaultScreen = X11::XDefaultScreen(display);
|
||||
|
||||
X11::XImage* image;
|
||||
image = X11::XGetImage(display, X11::XRootWindow(display, defaultScreen), x, y, 1, 1, AllPlanes, XYPixmap);
|
||||
color.pixel = XGetPixel(image, 0, 0);
|
||||
X11::XFree(image);
|
||||
|
||||
X11::XQueryColor(display, X11::XDefaultColormap(display, defaultScreen), &color);
|
||||
|
||||
Color32 outputColor;
|
||||
outputColor.R = color.red / 256;
|
||||
outputColor.G = color.green / 256;
|
||||
outputColor.B = color.blue / 256;
|
||||
return outputColor;
|
||||
}
|
||||
|
||||
void OnScreenUtilsXEventCallback(void* eventPtr)
|
||||
{
|
||||
X11::XEvent* event = (X11::XEvent*) eventPtr;
|
||||
X11::Display* display = (X11::Display*)LinuxPlatform::GetXDisplay();
|
||||
if (event->type == ButtonPress)
|
||||
{
|
||||
const Float2 cursorPos = Platform::GetMousePosition();
|
||||
const Color32 colorPicked = ScreenUtilities::GetColorAt(cursorPos);
|
||||
X11::XUngrabPointer(display, CurrentTime);
|
||||
ScreenUtilities::PickColorDone(colorPicked);
|
||||
LinuxPlatform::xEventRecieved.Unbind(OnScreenUtilsXEventCallback);
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenUtilities::PickColor()
|
||||
{
|
||||
PROFILE_CPU();
|
||||
X11::Display* display = (X11::Display*) LinuxPlatform::GetXDisplay();
|
||||
X11::Window rootWindow = X11::XRootWindow(display, X11::XDefaultScreen(display));
|
||||
|
||||
X11::Cursor cursor = XCreateFontCursor(display, 130);
|
||||
int grabbedPointer = X11::XGrabPointer(display, rootWindow, 0, ButtonPressMask, GrabModeAsync, GrabModeAsync, rootWindow, cursor, CurrentTime);
|
||||
if (grabbedPointer != GrabSuccess)
|
||||
{
|
||||
LOG(Error, "Failed to grab cursor for events.");
|
||||
X11::XFreeCursor(display, cursor);
|
||||
return;
|
||||
}
|
||||
|
||||
X11::XFreeCursor(display, cursor);
|
||||
LinuxPlatform::xEventRecieved.Bind(OnScreenUtilsXEventCallback);
|
||||
}
|
||||
|
||||
#elif PLATFORM_MAC
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#include <AppKit/AppKit.h>
|
||||
|
||||
Color32 ScreenUtilities::GetColorAt(const Float2& pos)
|
||||
{
|
||||
// TODO: implement ScreenUtilities for macOS
|
||||
return { 0, 0, 0, 255 };
|
||||
}
|
||||
|
||||
void ScreenUtilities::PickColor()
|
||||
{
|
||||
// This is what C# calls to start the color picking sequence
|
||||
// This should stop mouse clicks from working for one click, and that click is on the selected color
|
||||
// There is a class called NSColorSample that might implement that for you, but maybe not.
|
||||
}
|
||||
|
||||
#endif
|
||||
33
Source/Editor/Utilities/ScreenUtilities.h
Normal file
33
Source/Editor/Utilities/ScreenUtilities.h
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Core/Types/BaseTypes.h"
|
||||
#include "Engine/Core/Math/Color32.h"
|
||||
#include "Engine/Core/Math/Vector2.h"
|
||||
#include "Engine/Core/Delegate.h"
|
||||
|
||||
/// <summary>
|
||||
/// Platform-dependent screen utilities.
|
||||
/// </summary>
|
||||
API_CLASS(Static) class FLAXENGINE_API ScreenUtilities
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(ScreenUtilities);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pixel color at the specified coordinates.
|
||||
/// </summary>
|
||||
/// <param name="pos">Screen-space coordinate to read.</param>
|
||||
/// <returns>Pixel color at the specified coordinates.</returns>
|
||||
API_FUNCTION() static Color32 GetColorAt(const Float2& pos);
|
||||
|
||||
/// <summary>
|
||||
/// Starts async color picking. Color will be returned through PickColorDone event when the actions ends (user selected the final color with a mouse). When action is active, GetColorAt can be used to read the current value.
|
||||
/// </summary>
|
||||
API_FUNCTION() static void PickColor();
|
||||
|
||||
/// <summary>
|
||||
/// Called when PickColor action is finished.
|
||||
/// </summary>
|
||||
API_EVENT() static Delegate<Color32> PickColorDone;
|
||||
};
|
||||
@@ -26,9 +26,8 @@ protected:
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Gets the text entered during the current frame.
|
||||
/// Gets the text entered during the current frame (Unicode format).
|
||||
/// </summary>
|
||||
/// <returns>The input text (Unicode).</returns>
|
||||
API_PROPERTY() StringView GetInputText() const
|
||||
{
|
||||
return StringView(_state.InputText, _state.InputTextLength);
|
||||
|
||||
@@ -58,7 +58,6 @@ public:
|
||||
/// <summary>
|
||||
/// Gets the position of the mouse in the screen-space coordinates.
|
||||
/// </summary>
|
||||
/// <returns>The mouse position</returns>
|
||||
API_PROPERTY() FORCE_INLINE Float2 GetPosition() const
|
||||
{
|
||||
return _state.MousePosition;
|
||||
@@ -72,7 +71,6 @@ public:
|
||||
/// <summary>
|
||||
/// Gets the delta position of the mouse in the screen-space coordinates.
|
||||
/// </summary>
|
||||
/// <returns>The mouse position delta</returns>
|
||||
API_PROPERTY() FORCE_INLINE Float2 GetPositionDelta() const
|
||||
{
|
||||
return _state.MousePosition - _prevState.MousePosition;
|
||||
@@ -81,7 +79,6 @@ public:
|
||||
/// <summary>
|
||||
/// Gets the mouse wheel change during the last frame.
|
||||
/// </summary>
|
||||
/// <returns>Mouse wheel value delta</returns>
|
||||
API_PROPERTY() FORCE_INLINE float GetScrollDelta() const
|
||||
{
|
||||
return _state.MouseWheelDelta;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "Engine/Platform/MemoryStats.h"
|
||||
#include "Engine/Platform/MessageBox.h"
|
||||
#include "Engine/Platform/FileSystem.h"
|
||||
#include "Engine/Platform/Window.h"
|
||||
#include "Engine/Platform/User.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/Types/DateTime.h"
|
||||
@@ -520,6 +521,21 @@ void PlatformBase::CreateGuid(Guid& result)
|
||||
result = Guid(dateThingHigh, randomThing | (sequentialThing << 16), cyclesThing, dateThingLow);
|
||||
}
|
||||
|
||||
Float2 PlatformBase::GetMousePosition()
|
||||
{
|
||||
const Window* win = Engine::MainWindow;
|
||||
if (win)
|
||||
return win->ClientToScreen(win->GetMousePosition());
|
||||
return Float2::Minimum;
|
||||
}
|
||||
|
||||
void PlatformBase::SetMousePosition(const Float2& position)
|
||||
{
|
||||
const Window* win = Engine::MainWindow;
|
||||
if (win)
|
||||
win->SetMousePosition(win->ScreenToClient(position));
|
||||
}
|
||||
|
||||
Float2 PlatformBase::GetVirtualDesktopSize()
|
||||
{
|
||||
return Platform::GetVirtualDesktopBounds().Size;
|
||||
|
||||
@@ -660,6 +660,18 @@ public:
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mouse cursor position in screen-space coordinates.
|
||||
/// </summary>
|
||||
/// <returns>Mouse cursor coordinates.</returns>
|
||||
API_PROPERTY() static Float2 GetMousePosition();
|
||||
|
||||
/// <summary>
|
||||
/// Sets the mouse cursor position in screen-space coordinates.
|
||||
/// </summary>
|
||||
/// <param name="position">Cursor position to set.</param>
|
||||
API_PROPERTY() static void SetMousePosition(const Float2& position);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the origin position and size of the monitor at the given screen-space location.
|
||||
/// </summary>
|
||||
|
||||
@@ -88,6 +88,7 @@ X11::Cursor Cursors[(int32)CursorType::MAX];
|
||||
X11::XcursorImage* CursorsImg[(int32)CursorType::MAX];
|
||||
Dictionary<StringAnsi, X11::KeyCode> KeyNameMap;
|
||||
Array<KeyboardKeys> KeyCodeMap;
|
||||
Delegate<void*> LinuxPlatform::xEventRecieved;
|
||||
|
||||
// Message boxes configuration
|
||||
#define LINUX_DIALOG_MIN_BUTTON_WIDTH 64
|
||||
@@ -2231,10 +2232,12 @@ void LinuxPlatform::Tick()
|
||||
{
|
||||
X11::XEvent event;
|
||||
X11::XNextEvent(xDisplay, &event);
|
||||
|
||||
if (X11::XFilterEvent(&event, 0))
|
||||
continue;
|
||||
|
||||
// External event handling
|
||||
xEventRecieved(&event);
|
||||
|
||||
LinuxWindow* window;
|
||||
switch (event.type)
|
||||
{
|
||||
@@ -2639,10 +2642,8 @@ Float2 LinuxPlatform::GetMousePosition()
|
||||
{
|
||||
if (!xDisplay)
|
||||
return Float2::Zero;
|
||||
|
||||
int32 x, y;
|
||||
int32 x = 0, y = 0;
|
||||
uint32 screenCount = (uint32)X11::XScreenCount(xDisplay);
|
||||
|
||||
for (uint32 i = 0; i < screenCount; i++)
|
||||
{
|
||||
X11::Window outRoot, outChild;
|
||||
@@ -2651,7 +2652,6 @@ Float2 LinuxPlatform::GetMousePosition()
|
||||
if (X11::XQueryPointer(xDisplay, X11::XRootWindow(xDisplay, i), &outRoot, &outChild, &x, &y, &childX, &childY, &mask))
|
||||
break;
|
||||
}
|
||||
|
||||
return Float2((float)x, (float)y);
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,11 @@ public:
|
||||
/// <returns>The user home directory.</returns>
|
||||
static const String& GetHomeDirectory();
|
||||
|
||||
/// <summary>
|
||||
/// An event that is fired when an XEvent is received during platform tick.
|
||||
/// </summary>
|
||||
static Delegate<void*> xEventRecieved;
|
||||
|
||||
public:
|
||||
|
||||
// [UnixPlatform]
|
||||
|
||||
@@ -158,27 +158,6 @@ void UWPPlatform::OpenUrl(const StringView& url)
|
||||
// TODO: add support for OpenUrl on UWP
|
||||
}
|
||||
|
||||
Float2 UWPPlatform::GetMousePosition()
|
||||
{
|
||||
// Use the main window
|
||||
auto win = Engine::MainWindow;
|
||||
if (win)
|
||||
{
|
||||
return win->ClientToScreen(win->GetMousePosition());
|
||||
}
|
||||
return Float2::Minimum;
|
||||
}
|
||||
|
||||
void UWPPlatform::SetMousePosition(const Float2& pos)
|
||||
{
|
||||
// Use the main window
|
||||
auto win = Engine::MainWindow;
|
||||
if (win)
|
||||
{
|
||||
win->SetMousePosition(win->ScreenToClient(pos));
|
||||
}
|
||||
}
|
||||
|
||||
Float2 UWPPlatform::GetDesktopSize()
|
||||
{
|
||||
Float2 result;
|
||||
|
||||
@@ -37,8 +37,6 @@ public:
|
||||
static bool GetHasFocus();
|
||||
static bool CanOpenUrl(const StringView& url);
|
||||
static void OpenUrl(const StringView& url);
|
||||
static Float2 GetMousePosition();
|
||||
static void SetMousePosition(const Float2& pos);
|
||||
static Rectangle GetMonitorBounds(const Float2& screenPos);
|
||||
static Float2 GetDesktopSize();
|
||||
static Rectangle GetVirtualDesktopBounds();
|
||||
|
||||
@@ -109,7 +109,7 @@ public:
|
||||
static void CreateGuid(Guid& result);
|
||||
static String GetMainDirectory();
|
||||
static String GetExecutableFilePath();
|
||||
static struct Guid GetUniqueDeviceId();
|
||||
static Guid GetUniqueDeviceId();
|
||||
static String GetWorkingDirectory();
|
||||
static bool SetWorkingDirectory(const String& path);
|
||||
static void FreeLibrary(void* handle);
|
||||
|
||||
@@ -825,6 +825,18 @@ void WindowsPlatform::OpenUrl(const StringView& url)
|
||||
::ShellExecuteW(nullptr, TEXT("open"), *url, nullptr, nullptr, SW_SHOWNORMAL);
|
||||
}
|
||||
|
||||
Float2 WindowsPlatform::GetMousePosition()
|
||||
{
|
||||
POINT cursorPos;
|
||||
GetCursorPos(&cursorPos);
|
||||
return Float2((float)cursorPos.x, (float)cursorPos.y);
|
||||
}
|
||||
|
||||
void WindowsPlatform::SetMousePosition(const Float2& pos)
|
||||
{
|
||||
::SetCursorPos((int)pos.X, (int)pos.Y);
|
||||
}
|
||||
|
||||
struct GetMonitorBoundsData
|
||||
{
|
||||
Float2 Pos;
|
||||
|
||||
@@ -71,6 +71,8 @@ public:
|
||||
static bool GetHasFocus();
|
||||
static bool CanOpenUrl(const StringView& url);
|
||||
static void OpenUrl(const StringView& url);
|
||||
static Float2 GetMousePosition();
|
||||
static void SetMousePosition(const Float2& pos);
|
||||
static Rectangle GetMonitorBounds(const Float2& screenPos);
|
||||
static Float2 GetDesktopSize();
|
||||
static Rectangle GetVirtualDesktopBounds();
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include "Engine/Graphics/GPUResourceProperty.h"
|
||||
#include "Engine/Graphics/GPUDevice.h"
|
||||
#include "Engine/Graphics/GPUSwapChain.h"
|
||||
#include "Engine/Engine/Engine.h"
|
||||
#include "Engine/Threading/ThreadPoolTask.h"
|
||||
#include "Engine/Engine/Globals.h"
|
||||
#if COMPILE_WITH_TEXTURE_TOOL
|
||||
|
||||
Reference in New Issue
Block a user