Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Building a Lightweight Custom UI Toolkit from Scratch in C and C++

Tech May 14 1
#ifndef MRUI_H
#define MRUI_H

#include <Windows.h>

/**
 * Allocate a block of heap memory.
 *
 * @param dwBytes Number of bytes to allocate.
 * @return Pointer to the allocated memory, or NULL on failure.
 */
EXTERN_C LPVOID mruiMemAlloc(SIZE_T dwBytes);

/**
 * Free a previously allocated memory block.
 *
 * @param hMem Handle to the memory block.
 * @return TRUE on success, FALSE on failure.
 */
EXTERN_C BOOL mruiMemRelease(HANDLE hMem);

/**
 * Verify whether a memory handle points to a valid block.
 *
 * @param hMem Handle to the memory block.
 * @return TRUE if valid, FALSE otherwise.
 */
EXTERN_C BOOL mruiMemIsValid(HANDLE hMem);

/**
 * Instantiate a new child control.
 *
 * @param hParent Handle of the parent container.
 * @return Handle to the new control, or NULL on failure.
 */
EXTERN_C HANDLE mruiChildCreate(HANDLE hParent);

/**
 * Instantiate a top-level window.
 *
 * @param dwExStyle Extended window style.
 * @param dwStyle Window style.
 * @param lpClassName Registered window class name.
 * @param lpWindowName Window title.
 * @return Handle to the new window control, or NULL on failure.
 */
EXTERN_C HANDLE mruiWndCreate(DWORD dwExStyle, DWORD dwStyle, LPCWSTR lpClassName, LPCWSTR lpWindowName);

/**
 * Destroy a control and release its memory.
 *
 * @param hControl Handle to the control.
 * @return TRUE on success, FALSE on failure.
 */
EXTERN_C BOOL mruiDestroy(HANDLE hControl);

/**
 * Enter the application message loop.
 *
 * @param hControl Optional handle to a window or control.
 * @return Exit code on success, 0 on failure.
 */
EXTERN_C LRESULT mruiRunLoop(HANDLE hControl);

/**
 * Reparent a control to a new parent.
 *
 * @param hControl Handle to the control.
 * @param hParent Handle to the new parent.
 * @return TRUE on success, FALSE on failure.
 */
EXTERN_C BOOL mruiReparent(HANDLE hControl, HANDLE hParent);

/**
 * Retrieve the parent of a control.
 *
 * @param hControl Handle to the control.
 * @return Handle to the parent, or NULL if none.
 */
EXTERN_C HANDLE mruiQueryParent(HANDLE hControl);

/**
 * Detach a control from its parent without destroying it.
 *
 * @param hControl Handle to the control.
 * @return TRUE on success, FALSE on failure.
 */
EXTERN_C BOOL mruiDetach(HANDLE hControl);

/**
 * Install an event filter callback.
 *
 * @param hControl Handle to the control.
 * @param filter Function pointer with signature BOOL(*)(LPVOID object, UINT message, LPVOID arg1, LPVOID arg2, LPVOID arg3).
 * @return TRUE on success, FALSE on failure.
 */
EXTERN_C BOOL mruiInstallFilter(HANDLE hControl, LPVOID filter);

/**
 * Get the current event filter callback.
 *
 * @param hControl Handle to the control.
 * @return Filter function pointer, or NULL.
 */
EXTERN_C LPVOID mruiQueryFilter(HANDLE hControl);

/**
 * Manually invoke the event filter for a control.
 *
 * @param hControl Handle to the control.
 * @param message Event identifier.
 * @param arg1 First argument.
 * @param arg2 Second argument.
 * @param arg3 Third argument.
 * @return Return value of the filter, or FALSE on failure.
 */
EXTERN_C LRESULT mruiDispatchFilter(HANDLE hControl, UINT message, LPVOID arg1, LPVOID arg2, LPVOID arg3);

/**
 * Associate a custom class pointer with a control.
 *
 * @param hControl Handle to the control.
 * @param handle User-defined pointer.
 * @return TRUE on success, FALSE on failure.
 */
EXTERN_C BOOL mruiSetUserPtr(HANDLE hControl, LPVOID handle);

/**
 * Retrieve the custom class pointer associated with a control.
 *
 * @param hControl Handle to the control.
 * @return User-defined pointer, or NULL.
 */
EXTERN_C LPVOID mruiQueryUserPtr(HANDLE hControl);

/**
 * Assign an application-defined ID to a control.
 *
 * @param hControl Handle to the control.
 * @param id Identifier.
 * @return TRUE on success, FALSE on failure.
 */
EXTERN_C BOOL mruiSetId(HANDLE hControl, LPVOID id);

/**
 * Get the application-defined ID of a control.
 *
 * @param hControl Handle to the control.
 * @return ID pointer, or NULL.
 */
EXTERN_C LPVOID mruiQueryId(HANDLE hControl);

/**
 * Check whether a handle refers to a valid control.
 *
 * @param hControl Handle to inspect.
 * @return TRUE if valid, FALSE otherwise.
 */
EXTERN_C BOOL mruiIsValidHandle(HANDLE hControl);

/**
 * Check whether a handle refers to a window control.
 *
 * @param hControl Handle to inspect.
 * @return TRUE if window, FALSE otherwise.
 */
EXTERN_C BOOL mruiIsWnd(HANDLE hControl);

/**
 * Check whether a handle refers to a child control.
 *
 * @param hControl Handle to inspect.
 * @return TRUE if child control, FALSE otherwise.
 */
EXTERN_C BOOL mruiIsChild(HANDLE hControl);

/**
 * Invalidate a rectangular area of a control for repainting.
 *
 * @param hControl Handle to the control.
 * @param lpRect Rectangle to invalidate; NULL means the entire control.
 * @return TRUE on success, FALSE on failure.
 */
EXTERN_C BOOL mruiInvalidateRect(HANDLE hControl, LPRECT lpRect);

/**
 * Obtain the control's rectangle relative to the screen.
 *
 * @param hControl Handle to the control.
 * @param lpRect Output RECT.
 * @return TRUE on success, FALSE on failure.
 */
EXTERN_C BOOL mruiScreenRect(HANDLE hControl, LPRECT lpRect);

/**
 * Obtain the client area rectangle of the control.
 *
 * @param hControl Handle to the control.
 * @param lpRect Output RECT.
 * @return TRUE on success, FALSE on failure.
 */
EXTERN_C BOOL mruiClientRect(HANDLE hControl, LPRECT lpRect);

/**
 * Obtain the control's local bounding rectangle.
 *
 * @param hControl Handle to the control.
 * @param lpRect Output RECT.
 * @return TRUE on success, FALSE on failure.
 */
EXTERN_C BOOL mruiLocalRect(HANDLE hControl, LPRECT lpRect);

/**
 * Retrieve the dirty rectangle during a paint event.
 *
 * @param hControl Handle to the control.
 * @param lpRect Output RECT.
 * @return TRUE on success, FALSE on failure.
 */
EXTERN_C BOOL mruiDirtyRect(HANDLE hControl, LPRECT lpRect);

/**
 * Set the geometry (position and size) of a control.
 *
 * @param hControl Handle to the control.
 * @param x Left coordinate.
 * @param y Top coordinate.
 * @param width Width.
 * @param height Height.
 * @return TRUE on success, FALSE on failure.
 */
EXTERN_C BOOL mruiSetRect(HANDLE hControl, LONG x, LONG y, LONG width, LONG height);

/**
 * Determine whether the control belongs to a layered window.
 *
 * @param hControl Handle to the control.
 * @return TRUE if layered, FALSE otherwise.
 */
EXTERN_C BOOL mruiIsLayWin(HANDLE hControl);

/**
 * Determine whether the mouse cursor is currently over the control.
 *
 * @param hControl Handle to the control.
 * @return TRUE if mouse is inside the control's rectangle, FALSE otherwise.
 */
EXTERN_C BOOL mruiHitTest(HANDLE hControl);

/**
 * Get the owning window handle for a control.
 *
 * @param hControl Handle to the control.
 * @return Window handle, or NULL.
 */
EXTERN_C HANDLE mruiOwningWindow(HANDLE hControl);

/**
 * Request keyboard focus for a control.
 *
 * @param hControl Handle to the control.
 * @return TRUE on success, FALSE on failure.
 */
EXTERN_C BOOL mruiGrabFocus(HANDLE hControl);

/**
 * Return the control that currently owns the focus.
 *
 * @param hControl Handle to any control in the hierarchy.
 * @return Handle to the focused control, or NULL.
 */
EXTERN_C HANDLE mruiFocusedCtrl(HANDLE hControl);

/**
 * Check if a control has keyboard focus.
 *
 * @param hControl Handle to the control.
 * @return TRUE if focused, FALSE otherwise.
 */
EXTERN_C BOOL mruiHasFocus(HANDLE hControl);

/**
 * Revoke keyboard focus from the currently focused control.
 *
 * @param hControl Handle to the control whose hierarchy is affected.
 * @return TRUE on success, FALSE on failure.
 */
EXTERN_C BOOL mruiYieldFocus(HANDLE hControl);

/**
 * Capture mouse events.
 *
 * @param hControl Handle to the control.
 * @return TRUE on success, FALSE on failure.
 */
EXTERN_C BOOL mruiMouseCapture(HANDLE hControl);

/**
 * Retrieve the control that currently has mouse capture.
 *
 * @param hControl Handle to any control in the hierarchy.
 * @return Handle to the capturing control, or NULL.
 */
EXTERN_C HANDLE mruiCaptorCtrl(HANDLE hControl);

/**
 * Release mouse capture.
 *
 * @param hControl Handle to the control.
 * @return TRUE on success, FALSE on failure.
 */
EXTERN_C BOOL mruiMouseRelease(HANDLE hControl);

/**
 * Get the current mouse position relative to the control.
 *
 * @param hControl Handle to the control.
 * @param lpPoint Output POINT.
 * @return TRUE on success, FALSE on failure.
 */
EXTERN_C BOOL mruiCursorPos(HANDLE hControl, LPPOINT lpPoint);

/**
 * Obtain the device context for painting the control.
 *
 * @param hControl Handle to the control.
 * @return HDC, or NULL.
 */
EXTERN_C HDC mruiPaintDC(HANDLE hControl);

/**
 * Access the first child control.
 *
 * @param hControl Handle to the parent control.
 * @return Handle to the first child, or NULL.
 */
EXTERN_C HANDLE mruiFirstChild(HANDLE hControl);

/**
 * Access the last child control.
 *
 * @param hControl Handle to the parent control.
 * @return Handle to the last child, or NULL.
 */
EXTERN_C HANDLE mruiLastChild(HANDLE hControl);

/**
 * Access the control currently under the mouse pointer.
 *
 * @param hControl Handle to any control in the hierarchy.
 * @return Handle to the hovered control, or NULL.
 */
EXTERN_C HANDLE mruiHoveredCtrl(HANDLE hControl);

/**
 * Access the previous sibling control.
 *
 * @param hControl Handle to the control.
 * @return Handle to the previous sibling, or NULL.
 */
EXTERN_C HANDLE mruiPrevSibling(HANDLE hControl);

/**
 * Access the next sibling control.
 *
 * @param hControl Handle to the control.
 * @return Handle to the next sibling, or NULL.
 */
EXTERN_C HANDLE mruiNextSibling(HANDLE hControl);

/**
 * Raise the control to the top of its parent's Z-order.
 *
 * @param hControl Handle to the control.
 * @return TRUE on success, FALSE on failure.
 */
EXTERN_C BOOL mruiBringFront(HANDLE hControl);

/**
 * Retrieve the native HWND associated with a window control.
 *
 * @param hControl Handle to the control.
 * @return HWND, or NULL.
 */
EXTERN_C HWND mruiNativeWnd(HANDLE hControl);

/**
 * Map a native HWND back to a control handle.
 *
 * @param hWnd Window handle.
 * @return Control handle, or NULL.
 */
EXTERN_C HANDLE mruiFromHwnd(HWND hWnd);

/**
 * Show or hide a control.
 *
 * @param hControl Handle to the control.
 * @param visible TRUE to show, FALSE to hide.
 * @return TRUE on success, FALSE on failure.
 */
EXTERN_C BOOL mruiSetVisible(HANDLE hControl, BOOL visible);

/**
 * Query the visible state of a control.
 *
 * @param hControl Handle to the control.
 * @return TRUE if visible, FALSE otherwise.
 */
EXTERN_C BOOL mruiIsVisible(HANDLE hControl);

/**
 * Enable or disable a control.
 *
 * @param hControl Handle to the control.
 * @param disabled TRUE to disable, FALSE to enable.
 * @return TRUE on success, FALSE on failure.
 */
EXTERN_C BOOL mruiSetDisabled(HANDLE hControl, BOOL disabled);

/**
 * Query the disabled state of a control.
 *
 * @param hControl Handle to the control.
 * @return TRUE if disabled, FALSE otherwise.
 */
EXTERN_C BOOL mruiIsDisabled(HANDLE hControl);

/**
 * Toggle mouse-passthrough mode for a control.
 *
 * @param hControl Handle to the control.
 * @param penetr TRUE to pass mouse events through, FALSE otherwise.
 * @return TRUE on success, FALSE on failure.
 */
EXTERN_C BOOL mruiSetMouseProxy(HANDLE hControl, BOOL penetr);

/**
 * Query the mouse-passthrough mode.
 *
 * @param hControl Handle to the control.
 * @return TRUE if passthrough is active, FALSE otherwise.
 */
EXTERN_C BOOL mruiIsMouseProxy(HANDLE hControl);

/**
 * Set a boolean attribute on a control.
 *
 * @param hControl Handle to the control.
 * @param attribute Attribute identifier.
 * @param state TRUE or FALSE.
 * @return TRUE on success, FALSE on failure.
 */
EXTERN_C BOOL mruiSetAttr(HANDLE hControl, DWORD attribute, BOOL state);

/**
 * Retrieve a boolean attribute value.
 *
 * @param hControl Handle to the control.
 * @param attribute Attribute identifier.
 * @return Attribute value, or FALSE on failure.
 */
EXTERN_C BOOL mruiGetAttr(HANDLE hControl, DWORD attribute);

/**
 * Set the opacity level of a control.
 *
 * @param hControl Handle to the control.
 * @param opacity Opacity value (0.0 = transparent, 1.0 = opaque).
 * @return TRUE on success, FALSE on failure.
 */
EXTERN_C BOOL mruiSetOpacity(HANDLE hControl, FLOAT opacity);

/**
 * Query the current opacity level.
 *
 * @param hControl Handle to the control.
 * @return Opacity value, or 0.0 on failure.
 */
EXTERN_C FLOAT mruiGetOpacity(HANDLE hControl);

// Event type identifiers
enum mruiEvent {
	Event_None,
	Event_Close,
	Event_Timer,
	Event_KeyDown,
	Event_KeyUp,
	Event_KeyChar,
	Event_FocusIn,
	Event_FocusOut,
	Event_Render,
	Event_Move,
	Event_Resize,
	Event_Show,
	Event_Hide,
	Event_MouseDown,
	Event_MouseUp,
	Event_MouseDbl,
	Event_MouseMove,
	Event_MouseEnter,
	Event_MouseExit,
	Event_MouseWheel,
	Event_CaptureLost,
	Event_WinMsg,
	Event_User
};

// Mouse button identifiers
enum MouseBtn {
	Btn_None,
	Btn_Left,
	Btn_Right,
	Btn_Middle
};

// Attribute flags
enum CtrlAttr {
	Attr_None,
	Attr_AlphaChannel,
	Attr_UserFlag
};

#endif
#ifndef CCONTROL_H
#define CCONTROL_H

#include <Windows.h>
#include <mrui.h>
#include <CClass.h>
#include <CEvent.h>
#include <CPoint.h>
#include <CSize.h>
#include <CRect.h>

class CControl {
public:
	CControl(CControl * parent);
	~CControl();

public:
	template<class T>
	BOOL connect(LONG event, T * t, const BOOL(T::*f)(CControl&, CEvent&));

public:
	virtual BOOL filterEvent(CEvent & event);
	virtual BOOL closeEvent(CEvent & event);
	virtual BOOL timerEvent(CEvent & event);
	virtual BOOL keyDownEvent(CEvent & event);
	virtual BOOL keyUpEvent(CEvent & event);
	virtual BOOL keyCharEvent(CEvent & event);
	virtual BOOL focusInEvent(CEvent & event);
	virtual BOOL focusOutEvent(CEvent & event);
	virtual BOOL renderEvent(CEvent & event);
	virtual BOOL moveEvent(CEvent & event);
	virtual BOOL resizeEvent(CEvent & event);
	virtual BOOL showEvent(CEvent & event);
	virtual BOOL hideEvent(CEvent & event);
	virtual BOOL mouseDownEvent(CEvent & event);
	virtual BOOL mouseUpEvent(CEvent & event);
	virtual BOOL mouseDblEvent(CEvent & event);
	virtual BOOL mouseMoveEvent(CEvent & event);
	virtual BOOL mouseEnterEvent(CEvent & event);
	virtual BOOL mouseExitEvent(CEvent & event);
	virtual BOOL mouseWheelEvent(CEvent & event);
	virtual BOOL captureLostEvent(CEvent & event);
	virtual BOOL winMsgEvent(CEvent & event);

public:
	const BOOL close();
	const BOOL remove();
	const BOOL setGeometry(CRect & rect);
	const BOOL setGeometry(CPoint & pt);
	const BOOL setGeometry(CSize & size);
	const BOOL setGeometry(CPoint & pt, CSize & size);
	const BOOL setGeometry(LONG x, LONG y, LONG width, LONG height);
	const BOOL move(CPoint & pt);
	const BOOL move(LONG x, LONG y);
	const BOOL resize(CSize & size);
	const BOOL resize(LONG width, LONG height);
	const CRect dirtyRect();
	const CRect screenRect();
	const CRect clientRect();
	const CRect localRect();
	const LONG x();
	const LONG y();
	const LONG width();
	const LONG height();
	const CPoint cursorPos();
	const BOOL setEnabled(BOOL enabled);
	const BOOL isEnabled();
	const BOOL setVisible(BOOL visible);
	const BOOL isVisible();
	const BOOL show();
	const BOOL hide();
	const BOOL setFocus();
	const BOOL clearFocus();
	const CControl * focused();
	const BOOL isFocused();
	const BOOL setParent(CControl * parent);
	const CControl * parent();
	const BOOL invalidate();
	const BOOL underMouse();
	const HDC hdc();
	const HWND nativeWnd();
	const CControl * window();
	const CControl * firstChild();
	const CControl * lastChild();
	const CControl * hovered();
	const CControl * prevSibling();
	const CControl * nextSibling();
	const BOOL isWindow();
	const BOOL isChildControl();
	const BOOL isLayWin();
	const BOOL raise();
	const BOOL captureMouse();
	const BOOL hasCapture();
	const BOOL releaseCapture();
	const BOOL setId(LPVOID id);
	const LPVOID id();
	const LRESULT run();
	const BOOL setAttr(INT attribute, BOOL state);
	const BOOL attr(INT attribute);
	const BOOL setOpacity(FLOAT opacity);
	const FLOAT opacity();
};

#endif
#ifndef CBUTTON_H
#define CBUTTON_H

#include <Windows.h>
#include <CClass.h>
#include <CControl.h>

enum ButtonState {
	STATE_NORMAL,
	STATE_HOVERED,
	STATE_PRESSED,
	STATE_DISABLED,
	STATE_POSTPROCESS
};

class CButton : public CControl {
public:
	CButton(CControl * parent = nullptr);

	virtual BOOL renderEvent(CEvent & event) override;
	virtual BOOL mouseDownEvent(CEvent & event) override;
	virtual BOOL mouseUpEvent(CEvent & event) override;
	virtual BOOL mouseEnterEvent(CEvent & event) override;
	virtual BOOL mouseExitEvent(CEvent & event) override;
	virtual BOOL captureLostEvent(CEvent & event) override;

private:
	void drawBackground(HDC dc, const RECT & rc, COLORREF bg, COLORREF border, const wchar_t* label);
	ButtonState m_state;
};

#endif // CBUTTON_H
#include <CButton.h>

CButton::CButton(CControl * parent) : CControl(parent) {
	m_state = STATE_NORMAL;
}

void CButton::drawBackground(HDC dc, const RECT & rc, COLORREF bg, COLORREF border, const wchar_t* label) {
	HBRUSH brushBg = CreateSolidBrush(bg);
	HBRUSH brushBorder = CreateSolidBrush(border);
	FillRect(dc, &rc, brushBg);
	FrameRect(dc, &rc, brushBorder);
	DeleteObject(brushBg);
	DeleteObject(brushBorder);
	DrawTextW(dc, label, (INT)wcslen(label), (LPRECT)&rc, DT_VCENTER | DT_SINGLELINE | DT_CENTER | DT_END_ELLIPSIS);
}

BOOL CButton::renderEvent(CEvent & event) {
	CRect dirty = dirtyRect();
	RECT rc = { dirty.left(), dirty.top(), dirty.right(), dirty.bottom() };

	switch (m_state) {
	case STATE_NORMAL:
		drawBackground(hdc(), rc, RGB(255, 255, 255), RGB(200, 200, 200), L"button");
		break;
	case STATE_HOVERED:
		drawBackground(hdc(), rc, RGB(230, 230, 230), RGB(111, 111, 255), L"button");
		break;
	case STATE_PRESSED:
		drawBackground(hdc(), rc, RGB(200, 200, 200), RGB(111, 119, 255), L"button");
		break;
	default:
		break;
	}
	return TRUE;
}

BOOL CButton::mouseDownEvent(CEvent & event) {
	captureMouse();
	m_state = STATE_PRESSED;
	invalidate();
	return TRUE;
}

BOOL CButton::mouseUpEvent(CEvent & event) {
	releaseCapture();
	m_state = underMouse() ? STATE_HOVERED : STATE_NORMAL;
	invalidate();
	return TRUE;
}

BOOL CButton::mouseEnterEvent(CEvent & event) {
	m_state = STATE_HOVERED;
	invalidate();
	return TRUE;
}

BOOL CButton::mouseExitEvent(CEvent & event) {
	m_state = STATE_NORMAL;
	invalidate();
	return TRUE;
}

BOOL CButton::captureLostEvent(CEvent & event) {
	m_state = underMouse() ? STATE_HOVERED : STATE_NORMAL;
	invalidate();
	return TRUE;
}
#include <Windows.h>
#include <tchar.h>
#include "CControl\CControl.h"
#include "CControl\CButton.h"

class MainWindow : public CControl {
public:
	MainWindow(CControl * parent = nullptr) : CControl(parent) {
		CButton * btn = new CButton(this);
		btn->setGeometry(100, 100, 100, 50);
		btn->setOpacity(0.5f);
		btn->connect(Event_MouseUp, this, &MainWindow::OnClick);
	}

	const BOOL OnClick(CControl & object, CEvent & event) {
		::MessageBoxA(nativeWnd(), "Button clicked", "Info", MB_OK);
		return FALSE;
	}

	virtual BOOL closeEvent(CEvent & event) override {
		PostQuitMessage(0);
		CControl::closeEvent(event);
		return TRUE;
	}
};

int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE, LPTSTR, int) {
	MainWindow wnd(nullptr);
	wnd.show();
	wnd.run();
	return 0;
}
Tags: cC++

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.