diff --git a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs
index 8209b63cf..0c81ecc51 100644
--- a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs
+++ b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs
@@ -1,8 +1,10 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using FlaxEditor.GUI.Input;
+using FlaxEditor.Windows;
using FlaxEngine;
using FlaxEngine.GUI;
+using System;
namespace FlaxEditor.GUI.Dialogs
{
@@ -25,6 +27,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 +37,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 +52,7 @@ namespace FlaxEditor.GUI.Dialogs
private TextBox _cHex;
private Button _cCancel;
private Button _cOK;
+ private IconButton _cEyedropper;
///
/// Gets the selected color.
@@ -104,6 +109,7 @@ namespace FlaxEditor.GUI.Dialogs
{
_initialValue = initialValue;
_useDynamicEditing = useDynamicEditing;
+ _activeEyedropper = false;
_value = Color.Transparent;
_onChanged = colorChanged;
_onClosed = pickerClosed;
@@ -192,10 +198,49 @@ namespace FlaxEditor.GUI.Dialogs
};
_cOK.Clicked += OnSubmit;
+ // Eyedropper button
+ _cEyedropper = new IconButton(_cOK.X - EyedropperMargin, _cHex.Bottom + PickerMargin, Editor.Instance.Icons.Add64, hideBorder: false)
+ {
+ Parent = this,
+ };
+ _cEyedropper.Clicked += OnEyedropStart;
+ _cEyedropper.Height = (_cValue.Bottom - _cEyedropper.Y) * 0.5f;
+ _cEyedropper.Width = _cEyedropper.Height;
+ _cEyedropper.X -= _cEyedropper.Width;
+ //_cEyedropper.SetColors(_cEyedropper.BackgroundColor);
+
// Set initial color
SelectedColor = initialValue;
}
+ private Color32 GetEyedropColor()
+ {
+ Int2 mousePosition = ScreenUtilities.GetScreenCursorPosition();
+ Color32 pixelColor = ScreenUtilities.GetPixelAt(mousePosition.X, mousePosition.Y);
+
+ return pixelColor;
+ }
+
+ private void ColorPicked(Color32 colorPicked)
+ {
+ _activeEyedropper = false;
+ SelectedColor = colorPicked;
+ ScreenUtilities.PickColorDone -= ColorPicked;
+ }
+
+ private void OnEyedropStart()
+ {
+ _activeEyedropper = true;
+ ScreenUtilities.PickColor();
+ ScreenUtilities.PickColorDone += ColorPicked;
+ }
+
+ private void UpdateEyedrop()
+ {
+ Color32 pixelColor = GetEyedropColor();
+ SelectedColor = pixelColor;
+ }
+
private void OnRGBAChanged()
{
if (_disableEvents)
@@ -221,6 +266,18 @@ namespace FlaxEditor.GUI.Dialogs
SelectedColor = color;
}
+
+ ///
+ public override void Update(float deltaTime)
+ {
+ base.Update(deltaTime);
+
+ if (_activeEyedropper)
+ {
+ UpdateEyedrop();
+ }
+ }
+
///
public override void Draw()
{
diff --git a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilities.h b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilities.h
new file mode 100644
index 000000000..01380e25b
--- /dev/null
+++ b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilities.h
@@ -0,0 +1,40 @@
+#pragma once
+
+#include "Engine/Core/Types/BaseTypes.h"
+#include "Engine/Core/Delegate.h"
+
+API_INJECT_CODE(cpp, "#include \"Editor/Utilities/ScreenUtilities/ScreenUtilities.h\"");
+
+///
+/// Platform-dependent screen utilties.
+///
+API_CLASS(Static, Name = "ScreenUtilities", Tag = "NativeInvokeUseName")
+class FLAXENGINE_API ScreenUtilities
+{
+public:
+ static struct FLAXENGINE_API ScriptingTypeInitializer TypeInitializer;
+
+ ///
+ /// Gets the pixel color at the specified coordinates.
+ ///
+ /// X Coordinate to read.
+ /// Y Coordinate to read.
+ /// Pixel color at the specified coordinates.
+ API_FUNCTION() static Color32 GetPixelAt(int32 x, int32 y);
+
+ ///
+ /// Gets the cursor position, in screen cooridnates.
+ ///
+ /// Cursor position, in screen coordinates.
+ API_FUNCTION() static Int2 GetScreenCursorPosition();
+
+ ///
+ /// Starts async color picking. Will return a color through ColorReturnCallback.
+ ///
+ /// Called when PickColor() is finished.
+ ///
+ API_EVENT() static Delegate PickColorDone;
+};
diff --git a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesLinux.cpp b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesLinux.cpp
new file mode 100644
index 000000000..6dc2103d9
--- /dev/null
+++ b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesLinux.cpp
@@ -0,0 +1,104 @@
+#if PLATFORM_LINUX
+
+#include "ScreenUtilities.h"
+#include "Engine/Core/Math/Color32.h"
+#include "Engine/Core/Math/Vector2.h"
+#include "Engine/Core/Delegate.h"
+#include "Engine/Core/Log.h"
+#include "Engine/Platform/Linux/LinuxPlatform.h"
+
+#include "Engine/Platform/Linux/IncludeX11.h"
+
+Color32 ScreenUtilities::GetPixelAt(int32 x, int32 y)
+{
+ X11::XColor color;
+ Color32 outputColor;
+
+ 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);
+ outputColor.R = color.red / 256;
+ outputColor.G = color.green / 256;
+ outputColor.B = color.blue / 256;
+
+ return outputColor;
+}
+
+Int2 ScreenUtilities::GetScreenCursorPosition()
+{
+ Int2 cursorPosition = { 0, 0 };
+ X11::Display* display = (X11::Display*) LinuxPlatform::GetXDisplay();
+ X11::Window rootWindow = X11::XRootWindow(display, X11::XDefaultScreen(display));
+
+ // Buffers (Some useful, some not.)
+ X11::Window rootWindowBuffer;
+ int rootX, rootY;
+ int winXBuffer, winYBuffer;
+ unsigned int maskBuffer;
+
+ int gotPointer = X11::XQueryPointer(display, rootWindow, &rootWindowBuffer, &rootWindowBuffer, &rootX, &rootY, &winXBuffer, &winYBuffer, &maskBuffer);
+ if (!gotPointer) {
+ LOG(Error, "Failed to find the mouse pointer (Are you using multiple displays?)");
+ return cursorPosition;
+ }
+
+ cursorPosition.X = rootX;
+ cursorPosition.Y = rootY;
+
+ return cursorPosition;
+}
+
+class ScreenUtilitiesLinux
+{
+public:
+ static void BlockAndReadMouse();
+ static void xEventHandler(void* event);
+};
+
+void ScreenUtilitiesLinux::xEventHandler(void* eventPtr) {
+ X11::XEvent* event = (X11::XEvent*) eventPtr;
+
+ X11::Display* display = (X11::Display*) LinuxPlatform::GetXDisplay();
+
+ if (event->type == ButtonPress) {
+ Int2 cursorPosition = ScreenUtilities::GetScreenCursorPosition();
+ Color32 colorPicked = ScreenUtilities::GetPixelAt(cursorPosition.X, cursorPosition.Y);
+
+ ScreenUtilities::PickColorDone(colorPicked); // Run the callback for picking colors being complete.
+ LinuxPlatform::xEventRecieved.Unbind(xEventHandler); // Unbind the event, we only want to handle one click event
+ X11::XUngrabPointer(display, CurrentTime);
+ }
+}
+
+void ScreenUtilitiesLinux::BlockAndReadMouse()
+{
+ 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(xEventHandler);
+}
+
+Delegate ScreenUtilities::PickColorDone;
+
+void ScreenUtilities::PickColor()
+{
+ ScreenUtilitiesLinux::BlockAndReadMouse();
+}
+
+#endif
\ No newline at end of file
diff --git a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesMac.cpp b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesMac.cpp
new file mode 100644
index 000000000..f774242ce
--- /dev/null
+++ b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesMac.cpp
@@ -0,0 +1,46 @@
+#if PLATFORM_MAC
+#include
+#include
+
+#include "ScreenUtilities.h"
+#include "Engine/Core/Math/Color32.h"
+#include "Engine/Core/Math/Vector2.h"
+#include "Engine/Core/Delegate.h"
+#include "Engine/Core/Log.h"
+
+Color32 ScreenUtilities::GetPixelAt(int32 x, int32 y)
+{
+ // Called from C# for live updates to the color.
+
+ return { 0, 0, 0, 255 };
+}
+
+Int2 ScreenUtilities::GetScreenCursorPosition()
+{
+ // Called from C# for live updates to the color.
+
+ return { 0, 0 };
+}
+
+class ScreenUtilitiesMac
+{
+public:
+ static void BlockAndReadMouse();
+};
+
+void ScreenUtilitiesMac::BlockAndReadMouse()
+{
+ // Maybe you don't need this if you go with NSColorSampler
+}
+
+Delegate ScreenUtilities::PickColorDone;
+
+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.
+ // It also might just work to copy the Linux Impl since Mac uses X as well, right?
+}
+
+#endif
diff --git a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesWindows.cpp b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesWindows.cpp
new file mode 100644
index 000000000..5af695c4d
--- /dev/null
+++ b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesWindows.cpp
@@ -0,0 +1,94 @@
+#if PLATFORM_WINDOWS
+
+#include "ScreenUtilities.h"
+#include "Engine/Core/Math/Color32.h"
+#include "Engine/Core/Math/Vector2.h"
+#include "Engine/Core/Delegate.h"
+#include "Engine/Core/Log.h"
+#include "Engine/Scripting/ManagedCLR/MCore.h"
+
+#include
+
+
+#pragma comment(lib, "Gdi32.lib")
+
+
+Color32 ScreenUtilities::GetPixelAt(int32 x, int32 y)
+{
+ HDC deviceContext = GetDC(NULL);
+ COLORREF color = GetPixel(deviceContext, x, y);
+ ReleaseDC(NULL, deviceContext);
+
+ Color32 returnColor = { GetRValue(color), GetGValue(color), GetBValue(color), 255 };
+ return returnColor;
+}
+
+Int2 ScreenUtilities::GetScreenCursorPosition()
+{
+ POINT cursorPos;
+ GetCursorPos(&cursorPos);
+
+ Int2 returnCursorPos = { cursorPos.x, cursorPos.y };
+ return returnCursorPos;
+}
+
+class ScreenUtilitiesWindows
+{
+public:
+ static void PickSelected();
+ static void BlockAndReadMouse();
+};
+
+void ScreenUtilitiesWindows::PickSelected() {
+ Int2 cursorPos = ScreenUtilities::GetScreenCursorPosition();
+ Color32 colorPicked = ScreenUtilities::GetPixelAt(cursorPos.X, cursorPos.Y);
+
+ // Push event with the picked color.
+ ScreenUtilities::PickColorDone(colorPicked);
+}
+
+static HHOOK _mouseCallbackHook;
+LRESULT CALLBACK ScreenUtilsMouseCallback(
+ _In_ int nCode,
+ _In_ WPARAM wParam,
+ _In_ LPARAM lParam
+)
+{
+ if (wParam != WM_LBUTTONDOWN) { // Return as early as possible.
+ return CallNextHookEx(NULL, nCode, wParam, lParam);
+ }
+
+ if (nCode < 0) {
+ return CallNextHookEx(NULL, nCode, wParam, lParam);
+ }
+
+
+ if (nCode >= 0 && wParam == WM_LBUTTONDOWN) { // Now try to run our code.
+ UnhookWindowsHookEx(_mouseCallbackHook);
+
+ ScreenUtilitiesWindows::PickSelected();
+ return 1;
+ }
+
+ return CallNextHookEx(NULL, nCode, wParam, lParam);
+}
+
+void ScreenUtilitiesWindows::BlockAndReadMouse()
+{
+ _mouseCallbackHook = SetWindowsHookEx(WH_MOUSE_LL, ScreenUtilsMouseCallback, NULL, NULL);
+ if (_mouseCallbackHook == NULL)
+ {
+ LOG(Warning, "Failed to set mouse hook.");
+ LOG(Warning, "Error: {0}", GetLastError());
+ }
+}
+
+Delegate ScreenUtilities::PickColorDone;
+
+void ScreenUtilities::PickColor()
+{
+ ScreenUtilitiesWindows::BlockAndReadMouse();
+}
+
+
+#endif
diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs
index 5f836e069..acc0cf605 100644
--- a/Source/Editor/Utilities/Utils.cs
+++ b/Source/Editor/Utilities/Utils.cs
@@ -1037,7 +1037,9 @@ namespace FlaxEditor.Utilities
/// The processed name path.
public static string GetAssetNamePath(string path)
{
- path = GetAssetNamePathWithExt(path);
+ var projectFolder = Globals.ProjectFolder;
+ if (path.StartsWith(projectFolder))
+ path = path.Substring(projectFolder.Length + 1);
return StringUtils.GetPathWithoutExtension(path);
}
diff --git a/Source/Engine/Platform/Linux/LinuxPlatform.cpp b/Source/Engine/Platform/Linux/LinuxPlatform.cpp
index 8015162ec..631b8d017 100644
--- a/Source/Engine/Platform/Linux/LinuxPlatform.cpp
+++ b/Source/Engine/Platform/Linux/LinuxPlatform.cpp
@@ -5,6 +5,7 @@
#include "LinuxPlatform.h"
#include "LinuxWindow.h"
#include "LinuxInput.h"
+#include "IncludeX11.h"
#include "Engine/Core/Log.h"
#include "Engine/Core/Types/Guid.h"
#include "Engine/Core/Types/String.h"
@@ -30,7 +31,6 @@
#include "Engine/Input/Input.h"
#include "Engine/Input/Mouse.h"
#include "Engine/Input/Keyboard.h"
-#include "IncludeX11.h"
#include
#include
#include
@@ -2217,6 +2217,8 @@ void LinuxPlatform::BeforeRun()
{
}
+Delegate LinuxPlatform::xEventRecieved;
+
void LinuxPlatform::Tick()
{
UnixPlatform::Tick();
@@ -2234,7 +2236,9 @@ void LinuxPlatform::Tick()
if (X11::XFilterEvent(&event, 0))
continue;
-
+
+ xEventRecieved(&event); // Fire this event, since we recieved an event.
+
LinuxWindow* window;
switch (event.type)
{
diff --git a/Source/Engine/Platform/Linux/LinuxPlatform.h b/Source/Engine/Platform/Linux/LinuxPlatform.h
index 89afe9292..486674652 100644
--- a/Source/Engine/Platform/Linux/LinuxPlatform.h
+++ b/Source/Engine/Platform/Linux/LinuxPlatform.h
@@ -33,6 +33,11 @@ public:
/// The user home directory.
static const String& GetHomeDirectory();
+ ///
+ /// An event that is fired when an XEvent is recieved by Flax.
+ ///
+ static Delegate xEventRecieved;
+
public:
// [UnixPlatform]
diff --git a/Source/Engine/UI/GUI/Common/IconButton.cs b/Source/Engine/UI/GUI/Common/IconButton.cs
new file mode 100644
index 000000000..e2652cd9f
--- /dev/null
+++ b/Source/Engine/UI/GUI/Common/IconButton.cs
@@ -0,0 +1,74 @@
+// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
+
+using System;
+
+namespace FlaxEngine.GUI
+{
+ ///
+ /// Button with an icon.
+ ///
+ public class IconButton : Button
+ {
+ ///
+ /// The sprite rendered on the button.
+ ///
+ public SpriteHandle ButtonSprite { get; set; }
+
+ ///
+ /// Whether or not to hide the border of the button.
+ ///
+ public bool HideBorder = true;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The sprite used by the button.
+ public IconButton(SpriteHandle buttonSprite)
+ : this(0, 0, buttonSprite)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Position X coordinate
+ /// Position Y coordinate
+ /// The sprite used by the button.
+ /// Width
+ /// Height
+ /// Whether or not to hide the border.
+ public IconButton(float x, float y, SpriteHandle buttonSprite, float width = 120, float height = DefaultHeight, bool hideBorder = true)
+ : base(x, y, width, height)
+ {
+ ButtonSprite = buttonSprite;
+ BackgroundBrush = new SpriteBrush(ButtonSprite);
+ HideBorder = hideBorder;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Position
+ /// Size
+ /// The sprite used by the button.
+ public IconButton(Float2 location, Float2 size, SpriteHandle buttonSprite)
+ : this(location.X, location.Y, buttonSprite, size.X, size.Y)
+ {
+ }
+
+ ///
+ /// Sets the colors of the button, taking into account the field.>
+ ///
+ /// The color to use.
+ public override void SetColors(Color color)
+ {
+ BackgroundColor = color;
+ BackgroundColorSelected = color.RGBMultiplied(0.8f);
+ BackgroundColorHighlighted = color.RGBMultiplied(1.2f);
+
+ BorderColor = HideBorder ? Color.Transparent : color.RGBMultiplied(0.5f);
+ BorderColorSelected = BorderColor;
+ BorderColorHighlighted = BorderColor;
+ }
+ }
+}
diff --git a/Source/Engine/Utilities/Screenshot.cpp b/Source/Engine/Utilities/Screenshot.cpp
index d67f8d123..fbde8043b 100644
--- a/Source/Engine/Utilities/Screenshot.cpp
+++ b/Source/Engine/Utilities/Screenshot.cpp
@@ -3,6 +3,7 @@
#include "Screenshot.h"
#include "Engine/Core/Log.h"
#include "Engine/Core/Math/Math.h"
+#include "Engine/Core/Math/Color32.h"
#include "Engine/Graphics/RenderTask.h"
#include "Engine/Platform/FileSystem.h"
#include "Engine/Graphics/Textures/TextureData.h"