mirror of
https://github.com/buserror/mii_emu.git
synced 2025-04-06 15:38:04 +00:00
libmui: Updated from upstream
Tons of internal changes Signed-off-by: Michel Pollet <buserror@gmail.com>
This commit is contained in:
parent
589a73f98c
commit
e9f96e1c9b
@ -1,10 +1,18 @@
|
||||
# MUI Version Changelog
|
||||
|
||||
## 1.3
|
||||
* Split the big mui.h header into smaller headers. It was just getting too big really.
|
||||
* Removed *incbin.h* -- this was incompatible with several linkers, and didn't work for webassemly. Now the fonts are just included as C arrays.
|
||||
* Introduced a new *mui_control_group*. Basically the list of control is now made of (optional) multiple groups. A normal window only uses one, but for tab controls for example, that'll be quite handy.
|
||||
* This introduces a new API to iterate the window/control list, you can selectively iterate all, or just the visible controls.
|
||||
* There is now a standard PUT file dialog, with a "New" folder button and all.
|
||||
* Added an optional close box to the window title bars.
|
||||
|
||||
## 1.2
|
||||
* More tweaks to the menus. Popup menus can be justified left/right/center. Removed quite a few fudge factors.
|
||||
* Added a notion of a control (per window) having the 'focus'. Currently listboxes and text edit boxes can have the focus.
|
||||
|
||||
## 1.1 -- since the release of the original verison
|
||||
## 1.1 -- since the release of the original version
|
||||
* Added support for horizontal scrollbars.
|
||||
* Added a faster (vectorized) version of an obvious 'cg' function.
|
||||
* Fixed a problem with mui_timers. Also typed it now.
|
||||
|
@ -37,6 +37,22 @@ $(TARGET_LIB) : $(MUI_OBJ) | $(LIB)
|
||||
@echo " AR $@"
|
||||
$(Q)$(AR) rcs $@ $^
|
||||
|
||||
.PHONY : fonts
|
||||
define font_to_h
|
||||
src/fonts/$(2).h : $(1)
|
||||
{ echo "// Autogenerated by the Makefile, do not edit"; \
|
||||
echo "#pragma once"; \
|
||||
xxd -n $(2) -i $$< |\
|
||||
sed 's/unsigned/static const unsigned/' ; \
|
||||
}>$$@
|
||||
$(OBJ)/mii.o : src/fonts/$(2).h
|
||||
fonts: src/fonts/$(2).h
|
||||
endef
|
||||
|
||||
$(eval $(call font_to_h,fonts/Charcoal_mui.ttf,mui_main_font))
|
||||
$(eval $(call font_to_h,fonts/typicon.ttf,mui_icon_font))
|
||||
$(eval $(call font_to_h,fonts/Geneva.ttf,mui_geneva_font))
|
||||
|
||||
#
|
||||
# The shell program is used to test the UI library using plugins
|
||||
# It is made using XCB and XKB libraries to have a minimal dependency
|
||||
@ -75,3 +91,13 @@ lsp:
|
||||
sh ../utils/clangd_gen.sh >compile_commands.json
|
||||
|
||||
-include $(OBJ)/*.d
|
||||
|
||||
DESTDIR ?= /usr/local
|
||||
|
||||
install: $(TARGET_LIB)
|
||||
install -d $(DESTDIR)/lib
|
||||
install -m 644 $(TARGET_LIB) $(DESTDIR)/lib
|
||||
install -d $(DESTDIR)/include/mui/mui $(DESTDIR)/include/mui/control/
|
||||
install -m 644 src/*.h $(DESTDIR)/include/mui
|
||||
install -m 644 src/mui/*.h $(DESTDIR)/include/mui/mui/
|
||||
install -m 644 src/control/*.h $(DESTDIR)/include/mui/control/
|
||||
|
@ -27,7 +27,7 @@ MUI_VERSION := ${shell \
|
||||
echo $$(git describe --tags --abbrev=0 2>/dev/null || \
|
||||
echo "(dev)") \
|
||||
$$(git log -1 --date=short --pretty="%h %cd")}
|
||||
CPPFLAGS += -DMUI_VERSION="\"$(MUI_VERSION)\""
|
||||
#CPPFLAGS += -DMUI_VERSION="\"$(MUI_VERSION)\""
|
||||
|
||||
OPTIMIZE ?= -O0 -g
|
||||
CFLAGS += --std=gnu99 -Wall -Wextra
|
||||
|
@ -1,379 +0,0 @@
|
||||
/**
|
||||
* @file incbin.h
|
||||
* @author Dale Weiler
|
||||
* @brief Utility for including binary files
|
||||
*
|
||||
* Facilities for including binary files into the current translation unit and
|
||||
* making use from them externally in other translation units.
|
||||
*/
|
||||
#ifndef INCBIN_HDR
|
||||
#define INCBIN_HDR
|
||||
#include <limits.h>
|
||||
|
||||
// Michel addition:
|
||||
// Allow the included file to have an extra zero, to include text files
|
||||
// as plain zero terminated strings
|
||||
#ifdef INCBIN_TRAILING_ZERO
|
||||
#define INCBIN_TRAIL INCBIN_BYTE "0\n"
|
||||
#else
|
||||
#define INCBIN_TRAIL
|
||||
#endif
|
||||
|
||||
#if defined(__AVX512BW__) || \
|
||||
defined(__AVX512CD__) || \
|
||||
defined(__AVX512DQ__) || \
|
||||
defined(__AVX512ER__) || \
|
||||
defined(__AVX512PF__) || \
|
||||
defined(__AVX512VL__) || \
|
||||
defined(__AVX512F__)
|
||||
# define INCBIN_ALIGNMENT_INDEX 6
|
||||
#elif defined(__AVX__) || \
|
||||
defined(__AVX2__)
|
||||
# define INCBIN_ALIGNMENT_INDEX 5
|
||||
#elif defined(__SSE__) || \
|
||||
defined(__SSE2__) || \
|
||||
defined(__SSE3__) || \
|
||||
defined(__SSSE3__) || \
|
||||
defined(__SSE4_1__) || \
|
||||
defined(__SSE4_2__) || \
|
||||
defined(__neon__)
|
||||
# define INCBIN_ALIGNMENT_INDEX 4
|
||||
#elif ULONG_MAX != 0xffffffffu
|
||||
# define INCBIN_ALIGNMENT_INDEX 3
|
||||
# else
|
||||
# define INCBIN_ALIGNMENT_INDEX 2
|
||||
#endif
|
||||
|
||||
/* Lookup table of (1 << n) where `n' is `INCBIN_ALIGNMENT_INDEX' */
|
||||
#define INCBIN_ALIGN_SHIFT_0 1
|
||||
#define INCBIN_ALIGN_SHIFT_1 2
|
||||
#define INCBIN_ALIGN_SHIFT_2 4
|
||||
#define INCBIN_ALIGN_SHIFT_3 8
|
||||
#define INCBIN_ALIGN_SHIFT_4 16
|
||||
#define INCBIN_ALIGN_SHIFT_5 32
|
||||
#define INCBIN_ALIGN_SHIFT_6 64
|
||||
|
||||
/* Actual alignment value */
|
||||
#define INCBIN_ALIGNMENT \
|
||||
INCBIN_CONCATENATE( \
|
||||
INCBIN_CONCATENATE(INCBIN_ALIGN_SHIFT, _), \
|
||||
INCBIN_ALIGNMENT_INDEX)
|
||||
|
||||
/* Stringize */
|
||||
#define INCBIN_STR(X) \
|
||||
#X
|
||||
#define INCBIN_STRINGIZE(X) \
|
||||
INCBIN_STR(X)
|
||||
/* Concatenate */
|
||||
#define INCBIN_CAT(X, Y) \
|
||||
X ## Y
|
||||
#define INCBIN_CONCATENATE(X, Y) \
|
||||
INCBIN_CAT(X, Y)
|
||||
/* Deferred macro expansion */
|
||||
#define INCBIN_EVAL(X) \
|
||||
X
|
||||
#define INCBIN_INVOKE(N, ...) \
|
||||
INCBIN_EVAL(N(__VA_ARGS__))
|
||||
|
||||
/* Green Hills uses a different directive for including binary data */
|
||||
#if defined(__ghs__)
|
||||
# if (__ghs_asm == 2)
|
||||
# define INCBIN_MACRO ".file"
|
||||
/* Or consider the ".myrawdata" entry in the ld file */
|
||||
# else
|
||||
# define INCBIN_MACRO "\tINCBIN"
|
||||
# endif
|
||||
#else
|
||||
# define INCBIN_MACRO ".incbin"
|
||||
#endif
|
||||
|
||||
#ifndef _MSC_VER
|
||||
# define INCBIN_ALIGN \
|
||||
__attribute__((aligned(INCBIN_ALIGNMENT)))
|
||||
#else
|
||||
# define INCBIN_ALIGN __declspec(align(INCBIN_ALIGNMENT))
|
||||
#endif
|
||||
|
||||
#if defined(__arm__) || /* GNU C and RealView */ \
|
||||
defined(__arm) || /* Diab */ \
|
||||
defined(_ARM) /* ImageCraft */
|
||||
# define INCBIN_ARM
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
/* Utilize .balign where supported */
|
||||
# define INCBIN_ALIGN_HOST ".balign " INCBIN_STRINGIZE(INCBIN_ALIGNMENT) "\n"
|
||||
# define INCBIN_ALIGN_BYTE ".balign 1\n"
|
||||
#elif defined(INCBIN_ARM)
|
||||
/*
|
||||
* On arm assemblers, the alignment value is calculated as (1 << n) where `n' is
|
||||
* the shift count. This is the value passed to `.align'
|
||||
*/
|
||||
# define INCBIN_ALIGN_HOST ".align " INCBIN_STRINGIZE(INCBIN_ALIGNMENT_INDEX) "\n"
|
||||
# define INCBIN_ALIGN_BYTE ".align 0\n"
|
||||
#else
|
||||
/* We assume other inline assembler's treat `.align' as `.balign' */
|
||||
# define INCBIN_ALIGN_HOST ".align " INCBIN_STRINGIZE(INCBIN_ALIGNMENT) "\n"
|
||||
# define INCBIN_ALIGN_BYTE ".align 1\n"
|
||||
#endif
|
||||
|
||||
/* INCBIN_CONST is used by incbin.c generated files */
|
||||
#if defined(__cplusplus)
|
||||
# define INCBIN_EXTERNAL extern "C"
|
||||
# define INCBIN_CONST extern const
|
||||
#else
|
||||
# define INCBIN_EXTERNAL extern
|
||||
# define INCBIN_CONST const
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Optionally override the linker section into which data is emitted.
|
||||
*
|
||||
* @warning If you use this facility, you'll have to deal with platform-specific linker output
|
||||
* section naming on your own
|
||||
*
|
||||
* Overriding the default linker output section, e.g for esp8266/Arduino:
|
||||
* @code
|
||||
* #define INCBIN_OUTPUT_SECTION ".irom.text"
|
||||
* #include "incbin.h"
|
||||
* INCBIN(Foo, "foo.txt");
|
||||
* // Data is emitted into program memory that never gets copied to RAM
|
||||
* @endcode
|
||||
*/
|
||||
#if !defined(INCBIN_OUTPUT_SECTION)
|
||||
# if defined(__APPLE__)
|
||||
# define INCBIN_OUTPUT_SECTION ".const_data"
|
||||
# else
|
||||
# define INCBIN_OUTPUT_SECTION ".rodata"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
/* The directives are different for Apple branded compilers */
|
||||
# define INCBIN_SECTION INCBIN_OUTPUT_SECTION "\n"
|
||||
# define INCBIN_GLOBAL(NAME) ".globl " INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME "\n"
|
||||
# define INCBIN_INT ".long "
|
||||
# define INCBIN_MANGLE "_"
|
||||
# define INCBIN_BYTE ".byte "
|
||||
# define INCBIN_TYPE(...)
|
||||
#else
|
||||
# define INCBIN_SECTION ".section " INCBIN_OUTPUT_SECTION "\n"
|
||||
# define INCBIN_GLOBAL(NAME) ".global " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME "\n"
|
||||
# if defined(__ghs__)
|
||||
# define INCBIN_INT ".word "
|
||||
# else
|
||||
# define INCBIN_INT ".int "
|
||||
# endif
|
||||
# if defined(__USER_LABEL_PREFIX__)
|
||||
# define INCBIN_MANGLE INCBIN_STRINGIZE(__USER_LABEL_PREFIX__)
|
||||
# else
|
||||
# define INCBIN_MANGLE ""
|
||||
# endif
|
||||
# if defined(INCBIN_ARM)
|
||||
/* On arm assemblers, `@' is used as a line comment token */
|
||||
# define INCBIN_TYPE(NAME) ".type " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME ", %object\n"
|
||||
# elif defined(__MINGW32__) || defined(__MINGW64__)
|
||||
/* Mingw doesn't support this directive either */
|
||||
# define INCBIN_TYPE(NAME)
|
||||
# else
|
||||
/* It's safe to use `@' on other architectures */
|
||||
# define INCBIN_TYPE(NAME) ".type " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME ", @object\n"
|
||||
# endif
|
||||
# define INCBIN_BYTE ".byte "
|
||||
#endif
|
||||
|
||||
/* List of style types used for symbol names */
|
||||
#define INCBIN_STYLE_CAMEL 0
|
||||
#define INCBIN_STYLE_SNAKE 1
|
||||
|
||||
/**
|
||||
* @brief Specify the prefix to use for symbol names.
|
||||
*
|
||||
* By default this is `g', producing symbols of the form:
|
||||
* @code
|
||||
* #include "incbin.h"
|
||||
* INCBIN(Foo, "foo.txt");
|
||||
*
|
||||
* // Now you have the following symbols:
|
||||
* // const unsigned char gFooData[];
|
||||
* // const unsigned char *const gFooEnd;
|
||||
* // const unsigned int gFooSize;
|
||||
* @endcode
|
||||
*
|
||||
* If however you specify a prefix before including: e.g:
|
||||
* @code
|
||||
* #define INCBIN_PREFIX incbin
|
||||
* #include "incbin.h"
|
||||
* INCBIN(Foo, "foo.txt");
|
||||
*
|
||||
* // Now you have the following symbols instead:
|
||||
* // const unsigned char incbinFooData[];
|
||||
* // const unsigned char *const incbinFooEnd;
|
||||
* // const unsigned int incbinFooSize;
|
||||
* @endcode
|
||||
*/
|
||||
#if !defined(INCBIN_PREFIX)
|
||||
# define INCBIN_PREFIX g
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Specify the style used for symbol names.
|
||||
*
|
||||
* Possible options are
|
||||
* - INCBIN_STYLE_CAMEL "CamelCase"
|
||||
* - INCBIN_STYLE_SNAKE "snake_case"
|
||||
*
|
||||
* Default option is *INCBIN_STYLE_CAMEL* producing symbols of the form:
|
||||
* @code
|
||||
* #include "incbin.h"
|
||||
* INCBIN(Foo, "foo.txt");
|
||||
*
|
||||
* // Now you have the following symbols:
|
||||
* // const unsigned char <prefix>FooData[];
|
||||
* // const unsigned char *const <prefix>FooEnd;
|
||||
* // const unsigned int <prefix>FooSize;
|
||||
* @endcode
|
||||
*
|
||||
* If however you specify a style before including: e.g:
|
||||
* @code
|
||||
* #define INCBIN_STYLE INCBIN_STYLE_SNAKE
|
||||
* #include "incbin.h"
|
||||
* INCBIN(foo, "foo.txt");
|
||||
*
|
||||
* // Now you have the following symbols:
|
||||
* // const unsigned char <prefix>foo_data[];
|
||||
* // const unsigned char *const <prefix>foo_end;
|
||||
* // const unsigned int <prefix>foo_size;
|
||||
* @endcode
|
||||
*/
|
||||
#if !defined(INCBIN_STYLE)
|
||||
# define INCBIN_STYLE INCBIN_STYLE_CAMEL
|
||||
#endif
|
||||
|
||||
/* Style lookup tables */
|
||||
#define INCBIN_STYLE_0_DATA Data
|
||||
#define INCBIN_STYLE_0_END End
|
||||
#define INCBIN_STYLE_0_SIZE Size
|
||||
#define INCBIN_STYLE_1_DATA _data
|
||||
#define INCBIN_STYLE_1_END _end
|
||||
#define INCBIN_STYLE_1_SIZE _size
|
||||
|
||||
/* Style lookup: returning identifier */
|
||||
#define INCBIN_STYLE_IDENT(TYPE) \
|
||||
INCBIN_CONCATENATE( \
|
||||
INCBIN_STYLE_, \
|
||||
INCBIN_CONCATENATE( \
|
||||
INCBIN_EVAL(INCBIN_STYLE), \
|
||||
INCBIN_CONCATENATE(_, TYPE)))
|
||||
|
||||
/* Style lookup: returning string literal */
|
||||
#define INCBIN_STYLE_STRING(TYPE) \
|
||||
INCBIN_STRINGIZE( \
|
||||
INCBIN_STYLE_IDENT(TYPE)) \
|
||||
|
||||
/* Generate the global labels by indirectly invoking the macro with our style
|
||||
* type and concatenating the name against them. */
|
||||
#define INCBIN_GLOBAL_LABELS(NAME, TYPE) \
|
||||
INCBIN_INVOKE( \
|
||||
INCBIN_GLOBAL, \
|
||||
INCBIN_CONCATENATE( \
|
||||
NAME, \
|
||||
INCBIN_INVOKE( \
|
||||
INCBIN_STYLE_IDENT, \
|
||||
TYPE))) \
|
||||
INCBIN_INVOKE( \
|
||||
INCBIN_TYPE, \
|
||||
INCBIN_CONCATENATE( \
|
||||
NAME, \
|
||||
INCBIN_INVOKE( \
|
||||
INCBIN_STYLE_IDENT, \
|
||||
TYPE)))
|
||||
|
||||
/**
|
||||
* @brief Externally reference binary data included in another translation unit.
|
||||
*
|
||||
* Produces three external symbols that reference the binary data included in
|
||||
* another translation unit.
|
||||
*
|
||||
* The symbol names are a concatenation of `INCBIN_PREFIX' before *NAME*; with
|
||||
* "Data", as well as "End" and "Size" after. An example is provided below.
|
||||
*
|
||||
* @param NAME The name given for the binary data
|
||||
*
|
||||
* @code
|
||||
* INCBIN_EXTERN(Foo);
|
||||
*
|
||||
* // Now you have the following symbols:
|
||||
* // extern const unsigned char <prefix>FooData[];
|
||||
* // extern const unsigned char *const <prefix>FooEnd;
|
||||
* // extern const unsigned int <prefix>FooSize;
|
||||
* @endcode
|
||||
*/
|
||||
#define INCBIN_EXTERN(NAME) \
|
||||
INCBIN_EXTERNAL const INCBIN_ALIGN unsigned char \
|
||||
INCBIN_CONCATENATE( \
|
||||
INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \
|
||||
INCBIN_STYLE_IDENT(DATA))[]; \
|
||||
INCBIN_EXTERNAL const INCBIN_ALIGN unsigned char *const \
|
||||
INCBIN_CONCATENATE( \
|
||||
INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \
|
||||
INCBIN_STYLE_IDENT(END)); \
|
||||
INCBIN_EXTERNAL const unsigned int \
|
||||
INCBIN_CONCATENATE( \
|
||||
INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \
|
||||
INCBIN_STYLE_IDENT(SIZE))
|
||||
|
||||
/**
|
||||
* @brief Include a binary file into the current translation unit.
|
||||
*
|
||||
* Includes a binary file into the current translation unit, producing three symbols
|
||||
* for objects that encode the data and size respectively.
|
||||
*
|
||||
* The symbol names are a concatenation of `INCBIN_PREFIX' before *NAME*; with
|
||||
* "Data", as well as "End" and "Size" after. An example is provided below.
|
||||
*
|
||||
* @param NAME The name to associate with this binary data (as an identifier.)
|
||||
* @param FILENAME The file to include (as a string literal.)
|
||||
*
|
||||
* @code
|
||||
* INCBIN(Icon, "icon.png");
|
||||
*
|
||||
* // Now you have the following symbols:
|
||||
* // const unsigned char <prefix>IconData[];
|
||||
* // const unsigned char *const <prefix>IconEnd;
|
||||
* // const unsigned int <prefix>IconSize;
|
||||
* @endcode
|
||||
*
|
||||
* @warning This must be used in global scope
|
||||
* @warning The identifiers may be different if INCBIN_STYLE is not default
|
||||
*
|
||||
* To externally reference the data included by this in another translation unit
|
||||
* please @see INCBIN_EXTERN.
|
||||
*/
|
||||
#ifdef _MSC_VER
|
||||
#define INCBIN(NAME, FILENAME) \
|
||||
INCBIN_EXTERN(NAME)
|
||||
#else
|
||||
#define INCBIN(NAME, FILENAME) \
|
||||
__asm__(INCBIN_SECTION \
|
||||
INCBIN_GLOBAL_LABELS(NAME, DATA) \
|
||||
INCBIN_ALIGN_HOST \
|
||||
INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(DATA) ":\n" \
|
||||
INCBIN_MACRO " \"" FILENAME "\"\n" \
|
||||
INCBIN_TRAIL \
|
||||
INCBIN_GLOBAL_LABELS(NAME, END) \
|
||||
INCBIN_ALIGN_BYTE \
|
||||
INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(END) ":\n" \
|
||||
INCBIN_BYTE "1\n" \
|
||||
INCBIN_GLOBAL_LABELS(NAME, SIZE) \
|
||||
INCBIN_ALIGN_HOST \
|
||||
INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(SIZE) ":\n" \
|
||||
INCBIN_INT INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(END) " - " \
|
||||
INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(DATA) "\n" \
|
||||
INCBIN_ALIGN_HOST \
|
||||
".text\n" \
|
||||
); \
|
||||
INCBIN_EXTERN(NAME)
|
||||
|
||||
#endif
|
||||
#endif
|
Binary file not shown.
42
libmui/src/control/box.h
Normal file
42
libmui/src/control/box.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* box.h
|
||||
*
|
||||
* Copyright (C) 2024 Michel Pollet <buserror@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mui/mui_types.h>
|
||||
#include <mui/mui_control.h>
|
||||
#include <mui/mui_text.h>
|
||||
|
||||
|
||||
/*
|
||||
* Create a static text box. Font is optional (default to the system main font),
|
||||
* flags corresponds to the MUI_TEXT_ALIGN_* * PLUS the extra(s) listed below.
|
||||
*/
|
||||
enum mui_textbox_e {
|
||||
// draw the frame around the text box
|
||||
MUI_CONTROL_TEXTBOX_FRAME = (1 << (MUI_TEXT_FLAGS_COUNT+1)),
|
||||
MUI_CONTROL_TEXTBOX_FLAGS_COUNT = (MUI_TEXT_FLAGS_COUNT+1),
|
||||
};
|
||||
mui_control_t *
|
||||
mui_textbox_new(
|
||||
mui_window_t * win,
|
||||
c2_rect_t frame,
|
||||
const char * text,
|
||||
const char * font,
|
||||
uint32_t flags );
|
||||
|
||||
mui_control_t *
|
||||
mui_groupbox_new(
|
||||
mui_window_t * win,
|
||||
c2_rect_t frame,
|
||||
const char * title,
|
||||
uint32_t flags );
|
||||
mui_control_t *
|
||||
mui_separator_new(
|
||||
mui_window_t * win,
|
||||
c2_rect_t frame);
|
34
libmui/src/control/button.h
Normal file
34
libmui/src/control/button.h
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* button.h
|
||||
*
|
||||
* Copyright (C) 2024 Michel Pollet <buserror@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mui/mui_types.h>
|
||||
#include <mui/mui_window.h>
|
||||
|
||||
|
||||
enum mui_button_style_e {
|
||||
MUI_BUTTON_STYLE_NORMAL = 0,
|
||||
MUI_BUTTON_STYLE_DEFAULT = 1,
|
||||
MUI_BUTTON_STYLE_RADIO,
|
||||
MUI_BUTTON_STYLE_CHECKBOX,
|
||||
};
|
||||
|
||||
mui_control_t *
|
||||
mui_button_new(
|
||||
mui_window_t * win,
|
||||
c2_rect_t frame,
|
||||
uint8_t style, // one of mui_button_style_e
|
||||
const char * title,
|
||||
uint32_t uid );
|
||||
// "align" is not implemented yet
|
||||
void
|
||||
mui_button_set_icon(
|
||||
mui_control_t * c,
|
||||
const char * icon,
|
||||
mui_text_e align );
|
25
libmui/src/control/drawable.h
Normal file
25
libmui/src/control/drawable.h
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* drawable.h
|
||||
*
|
||||
* Copyright (C) 2024 Michel Pollet <buserror@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mui/mui_types.h>
|
||||
#include <mui/mui_window.h>
|
||||
|
||||
|
||||
/* Drawable control is just an offscreen buffer (icon, pixel view) */
|
||||
mui_control_t *
|
||||
mui_drawable_control_new(
|
||||
mui_window_t * win,
|
||||
c2_rect_t frame,
|
||||
mui_drawable_t * dr,
|
||||
mui_drawable_t * mask,
|
||||
uint16_t flags);
|
||||
mui_drawable_t *
|
||||
mui_drawable_control_get_drawable(
|
||||
mui_control_t * c);
|
48
libmui/src/control/listbox.h
Normal file
48
libmui/src/control/listbox.h
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* listbox.h
|
||||
*
|
||||
* Copyright (C) 2024 Michel Pollet <buserror@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <mui/mui_types.h>
|
||||
#include <mui/mui_control.h>
|
||||
|
||||
struct mui_control_t;
|
||||
struct mui_window_t;
|
||||
struct mui_listbox_elem_t;
|
||||
|
||||
/* This is currently unused */
|
||||
typedef void (*mui_ldef_p)(
|
||||
struct mui_control_t * c,
|
||||
uint32_t elem_index,
|
||||
struct mui_listbox_elem_t * elem);
|
||||
|
||||
|
||||
typedef struct mui_listbox_elem_t {
|
||||
uint32_t disabled : 1;
|
||||
// currently this is a UTF8 string using the 'icons' font
|
||||
char icon[8]; // UTF8 icon
|
||||
// default 'LDEF' is to draw the 'elem' string
|
||||
void * elem; // char * or... ?
|
||||
} mui_listbox_elem_t;
|
||||
|
||||
DECLARE_C_ARRAY(mui_listbox_elem_t, mui_listbox_elems, 2);
|
||||
IMPLEMENT_C_ARRAY(mui_listbox_elems);
|
||||
|
||||
|
||||
mui_control_t *
|
||||
mui_listbox_new(
|
||||
mui_window_t * win,
|
||||
c2_rect_t frame,
|
||||
uint32_t uid );
|
||||
void
|
||||
mui_listbox_prepare(
|
||||
mui_control_t * c);
|
||||
mui_listbox_elems_t *
|
||||
mui_listbox_get_elems(
|
||||
mui_control_t * c);
|
32
libmui/src/control/popup.h
Normal file
32
libmui/src/control/popup.h
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* popup.h
|
||||
*
|
||||
* Copyright (C) 2024 Michel Pollet <buserror@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mui/mui_types.h>
|
||||
#include <mui/mui_control.h>
|
||||
#include <mui/mui_menu.h>
|
||||
|
||||
/* Popup menu control.
|
||||
* flags are MUI_TEXT_ALIGN_* -- however this corresponds to the margins
|
||||
* of the popup control itself when placed into it's 'frame' -- the
|
||||
* popup will be placed left,right,center of the frame rectangle depending.
|
||||
*/
|
||||
mui_control_t *
|
||||
mui_popupmenu_new(
|
||||
mui_window_t * win,
|
||||
c2_rect_t frame,
|
||||
const char * title,
|
||||
uint32_t uid,
|
||||
uint32_t flags);
|
||||
mui_menu_items_t *
|
||||
mui_popupmenu_get_items(
|
||||
mui_control_t * c);
|
||||
void
|
||||
mui_popupmenu_prepare(
|
||||
mui_control_t * c);
|
40
libmui/src/control/scrollbar.h
Normal file
40
libmui/src/control/scrollbar.h
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* scrollbar.h
|
||||
*
|
||||
* Copyright (C) 2024 Michel Pollet <buserror@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mui/mui_types.h>
|
||||
#include <mui/mui_control.h>
|
||||
|
||||
|
||||
|
||||
/* Page step and line step are optional, they default to '30' pixels and
|
||||
* the 'visible' area of the scrollbar, respectively.
|
||||
* If you want to for example have a scrollbar that scrolls by 5 when you
|
||||
* click the arrows, and by 20 when you click the bar, you would set the
|
||||
* line_step to 5, and the page_step to 20.
|
||||
*/
|
||||
mui_control_t *
|
||||
mui_scrollbar_new(
|
||||
mui_window_t * win,
|
||||
c2_rect_t frame,
|
||||
uint32_t uid,
|
||||
uint32_t line_step,
|
||||
uint32_t page_step);
|
||||
uint32_t
|
||||
mui_scrollbar_get_max(
|
||||
mui_control_t * c);
|
||||
void
|
||||
mui_scrollbar_set_max(
|
||||
mui_control_t * c,
|
||||
uint32_t max);
|
||||
void
|
||||
mui_scrollbar_set_page(
|
||||
mui_control_t * c,
|
||||
uint32_t page);
|
||||
|
52
libmui/src/control/textedit.h
Normal file
52
libmui/src/control/textedit.h
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* textedit.h
|
||||
*
|
||||
* Copyright (C) 2024 Michel Pollet <buserror@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mui/mui_types.h>
|
||||
#include <mui/mui_control.h>
|
||||
#include <control/box.h>
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Text editor control
|
||||
*/
|
||||
enum {
|
||||
// do we handle multi-line text? If zero, we only handle one line
|
||||
MUI_CONTROL_TEXTEDIT_VERTICAL = 1 << (MUI_CONTROL_TEXTBOX_FLAGS_COUNT+1),
|
||||
MUI_CONTROL_TEXTEDIT_FLAGS_COUNT = (MUI_CONTROL_TEXTBOX_FLAGS_COUNT+1),
|
||||
};
|
||||
|
||||
mui_control_t *
|
||||
mui_textedit_control_new(
|
||||
mui_window_t * win,
|
||||
c2_rect_t frame,
|
||||
uint32_t flags);
|
||||
void
|
||||
mui_textedit_set_text(
|
||||
mui_control_t * c,
|
||||
const char * text);
|
||||
void
|
||||
mui_textedit_set_selection(
|
||||
mui_control_t * c,
|
||||
uint start,
|
||||
uint end);
|
||||
/*
|
||||
* Get current selection
|
||||
*/
|
||||
void
|
||||
mui_textedit_get_selection(
|
||||
mui_control_t * c,
|
||||
uint * glyph_start,
|
||||
uint * glyph_end);
|
||||
uint
|
||||
mui_textedit_get_text(
|
||||
mui_control_t * c,
|
||||
char * text,
|
||||
uint len);
|
55429
libmui/src/fonts/mui_geneva_font.h
Normal file
55429
libmui/src/fonts/mui_geneva_font.h
Normal file
File diff suppressed because it is too large
Load Diff
8379
libmui/src/fonts/mui_icon_font.h
Normal file
8379
libmui/src/fonts/mui_icon_font.h
Normal file
File diff suppressed because it is too large
Load Diff
4631
libmui/src/fonts/mui_main_font.h
Normal file
4631
libmui/src/fonts/mui_main_font.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -21,7 +21,8 @@ mui_init(
|
||||
// colors)
|
||||
//memset(ui, 0, sizeof(*ui));
|
||||
ui->color.clear = MUI_COLOR(0xccccccff);
|
||||
ui->color.highlight = MUI_COLOR(0xd6fcc0ff);
|
||||
// ui->color.highlight = MUI_COLOR(0xc6fbc0ff);
|
||||
ui->color.highlight = MUI_COLOR(0xb6fbb0ff);
|
||||
ui->timer.map = 0;
|
||||
ui->carret_timer = 0xff;
|
||||
TAILQ_INIT(&ui->windows);
|
||||
|
1369
libmui/src/mui.h
1369
libmui/src/mui.h
File diff suppressed because it is too large
Load Diff
50
libmui/src/mui/mui_action.h
Normal file
50
libmui/src/mui/mui_action.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* mui_action.h
|
||||
*
|
||||
* Copyright (C) 2024 Michel Pollet <buserror@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mui/mui_types.h>
|
||||
|
||||
/*!
|
||||
* Actions are the provided way to add custom response to events for the
|
||||
* application; action handlers are called for a variety of things, from clicks
|
||||
* in controls, to menu selections, to window close etc.
|
||||
*
|
||||
* The 'what' parameter is a 4 character code, that can be used to identify
|
||||
* the action, and the 'param' is a pointer to a structure that depends on
|
||||
* the 'what' action (hopefully documented with that action constant)
|
||||
*
|
||||
* the 'cb_param' is specific to this action function pointer and is passed as
|
||||
* is to the callback, this is the pointer you pass to mui_window_add_action()
|
||||
*/
|
||||
typedef int (*mui_window_action_p)(
|
||||
struct mui_window_t * win,
|
||||
void * cb_param,
|
||||
uint32_t what,
|
||||
void * param);
|
||||
typedef int (*mui_control_action_p)(
|
||||
struct mui_control_t *c,
|
||||
void * cb_param,
|
||||
uint32_t what,
|
||||
void * param);
|
||||
/*
|
||||
* This is a standardized way of installing 'action' handlers onto windows
|
||||
* and controls. The 'current' field is used to prevent re-entrance. This structure
|
||||
* is opaque and is not accessible by the application, typically.
|
||||
*/
|
||||
typedef struct mui_action_t {
|
||||
STAILQ_ENTRY(mui_action_t) self;
|
||||
uint32_t current; // prevents re-entrance
|
||||
union {
|
||||
mui_window_action_p window_cb;
|
||||
mui_control_action_p control_cb;
|
||||
};
|
||||
void * cb_param;
|
||||
} mui_action_t;
|
||||
|
||||
typedef STAILQ_HEAD(, mui_action_t) mui_action_queue_t;
|
40
libmui/src/mui/mui_alert.h
Normal file
40
libmui/src/mui/mui_alert.h
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* mui_alert.h
|
||||
*
|
||||
* Copyright (C) 2024 Michel Pollet <buserror@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mui/mui_types.h>
|
||||
#include <mui/mui_window.h>
|
||||
|
||||
|
||||
/*
|
||||
* Alert dialog
|
||||
*/
|
||||
enum mui_alert_flag_e {
|
||||
MUI_ALERT_FLAG_OK = (1 << 0),
|
||||
MUI_ALERT_FLAG_CANCEL = (1 << 1),
|
||||
|
||||
MUI_ALERT_ICON_INFO = (1 << 8),
|
||||
|
||||
MUI_ALERT_INFO = (MUI_ALERT_FLAG_OK | MUI_ALERT_ICON_INFO),
|
||||
MUI_ALERT_WARN = (MUI_ALERT_FLAG_OK | MUI_ALERT_FLAG_CANCEL),
|
||||
};
|
||||
|
||||
enum {
|
||||
MUI_ALERT_BUTTON_OK = FCC('o','k',' ',' '),
|
||||
MUI_ALERT_BUTTON_CANCEL = FCC('c','a','n','c'),
|
||||
};
|
||||
|
||||
mui_window_t *
|
||||
mui_alert(
|
||||
struct mui_t * ui,
|
||||
c2_pt_t where, // (0,0) will center it
|
||||
const char * title,
|
||||
const char * message,
|
||||
uint16_t flags );
|
||||
|
173
libmui/src/mui/mui_control.h
Normal file
173
libmui/src/mui/mui_control.h
Normal file
@ -0,0 +1,173 @@
|
||||
/*
|
||||
* mui_control.h
|
||||
*
|
||||
* Copyright (C) 2024 Michel Pollet <buserror@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mui/mui_types.h>
|
||||
#include <mui/mui_event.h>
|
||||
#include <mui/mui_control_group.h>
|
||||
#include <mui/mui_ref.h>
|
||||
#include <mui/mui_window.h>
|
||||
|
||||
|
||||
enum mui_cdef_e {
|
||||
MUI_CDEF_INIT = 0, // param is NULL
|
||||
MUI_CDEF_DISPOSE, // param is NULL
|
||||
MUI_CDEF_DRAW, // param is mui_drawable_t*
|
||||
MUI_CDEF_EVENT, // param is mui_event_t*
|
||||
MUI_CDEF_SET_STATE, // param is int*
|
||||
MUI_CDEF_SET_VALUE, // param is int*
|
||||
MUI_CDEF_SET_FRAME, // param is c2_rect_t*
|
||||
MUI_CDEF_SET_TITLE, // param is char * (utf8)
|
||||
// Used when hot-key is pressed, change control value
|
||||
// to simulate a click
|
||||
MUI_CDEF_SELECT,
|
||||
// used when a window is selected, to set the focus to the
|
||||
// first control that can accept it
|
||||
MUI_CDEF_FOCUS, // param is int* with 0,1
|
||||
MUI_CDEF_CAN_FOCUS,// param is NULL, return true or false
|
||||
};
|
||||
typedef bool (*mui_cdef_p)(
|
||||
struct mui_control_t * c,
|
||||
uint8_t what,
|
||||
void * param);
|
||||
|
||||
|
||||
enum mui_control_state_e {
|
||||
MUI_CONTROL_STATE_NORMAL = 0,
|
||||
MUI_CONTROL_STATE_HOVER,
|
||||
MUI_CONTROL_STATE_CLICKED,
|
||||
MUI_CONTROL_STATE_DISABLED,
|
||||
MUI_CONTROL_STATE_COUNT
|
||||
};
|
||||
|
||||
enum mui_control_action_e {
|
||||
MUI_CONTROL_ACTION_NONE = 0,
|
||||
MUI_CONTROL_ACTION_VALUE_CHANGED = FCC('c','v','a','l'),
|
||||
MUI_CONTROL_ACTION_CLICKED = FCC('c','l','k','d'),
|
||||
MUI_CONTROL_ACTION_SELECT = FCC('c','s','e','l'),
|
||||
MUI_CONTROL_ACTION_DOUBLECLICK = FCC('c','d','c','l'),
|
||||
};
|
||||
|
||||
/*
|
||||
* Control record... this are the 'common' fields, most of the controls
|
||||
* have their own 'extended' record using their own fields.
|
||||
*/
|
||||
typedef struct mui_control_t {
|
||||
TAILQ_ENTRY(mui_control_t) self;
|
||||
mui_control_group_t * group; // group we belong to
|
||||
struct mui_window_t * win;
|
||||
mui_refqueue_t refs;
|
||||
mui_control_ref_t lock;
|
||||
mui_cdef_p cdef;
|
||||
uint32_t state;
|
||||
uint32_t type;
|
||||
uint32_t style;
|
||||
struct {
|
||||
uint hidden : 1, // combined with group->hidden
|
||||
hit_part : 8;
|
||||
} flags;
|
||||
uint32_t value;
|
||||
uint32_t uid;
|
||||
uint32_t uid_mask; // for radio buttons
|
||||
c2_rect_t frame;
|
||||
mui_key_equ_t key_equ; // keystroke to select this control
|
||||
char * title;
|
||||
mui_action_queue_t actions;
|
||||
} mui_control_t;
|
||||
|
||||
/*
|
||||
* Control related
|
||||
*/
|
||||
/*
|
||||
* This is the 'low level' control creation function, you can pass the
|
||||
* 'cdef' function pointer and a control 'type' that will be passed to it,
|
||||
* so you can implement variants of controls.
|
||||
* The instance_size is the size of the extended control record, if any.
|
||||
*/
|
||||
mui_control_t *
|
||||
mui_control_new(
|
||||
mui_window_t * win,
|
||||
uint32_t type, // specific to the CDEF
|
||||
mui_cdef_p cdef,
|
||||
c2_rect_t frame,
|
||||
const char * title,
|
||||
uint32_t uid,
|
||||
uint32_t instance_size );
|
||||
void
|
||||
mui_control_dispose(
|
||||
mui_control_t * c );
|
||||
uint32_t
|
||||
mui_control_get_type(
|
||||
mui_control_t * c );
|
||||
uint32_t
|
||||
mui_control_get_uid(
|
||||
mui_control_t * c );
|
||||
mui_control_t *
|
||||
mui_control_locate(
|
||||
mui_window_t * win,
|
||||
c2_pt_t pt );
|
||||
mui_control_t *
|
||||
mui_control_get_by_id(
|
||||
mui_window_t * win,
|
||||
uint32_t uid );
|
||||
void
|
||||
mui_control_inval(
|
||||
mui_control_t * c );
|
||||
void
|
||||
mui_control_set_frame(
|
||||
mui_control_t * c,
|
||||
c2_rect_t * frame );
|
||||
void
|
||||
mui_control_action(
|
||||
mui_control_t * c,
|
||||
uint32_t what,
|
||||
void * param );
|
||||
void
|
||||
mui_control_set_action(
|
||||
mui_control_t * c,
|
||||
mui_control_action_p cb,
|
||||
void * param );
|
||||
void
|
||||
mui_control_set_state(
|
||||
mui_control_t * c,
|
||||
uint32_t state );
|
||||
uint32_t
|
||||
mui_control_get_state(
|
||||
mui_control_t * c );
|
||||
|
||||
int32_t
|
||||
mui_control_get_value(
|
||||
mui_control_t * c);
|
||||
int32_t
|
||||
mui_control_set_value(
|
||||
mui_control_t * c,
|
||||
int32_t selected);
|
||||
const char *
|
||||
mui_control_get_title(
|
||||
mui_control_t * c );
|
||||
void
|
||||
mui_control_set_title(
|
||||
mui_control_t * c,
|
||||
const char * text );
|
||||
/* Sets the focus to control 'c' in that window, return true if that
|
||||
* control was able to take the focus, or false if it wasn't (for example any
|
||||
* control that are not focusable will return false)
|
||||
*/
|
||||
bool
|
||||
mui_control_set_focus(
|
||||
mui_control_t * c );
|
||||
/* Returns true if the control has the focus */
|
||||
bool
|
||||
mui_control_has_focus(
|
||||
mui_control_t * c );
|
||||
/* Switch focus to the next/previous control in the window */
|
||||
mui_control_t *
|
||||
mui_control_switch_focus(
|
||||
mui_window_t * win,
|
||||
int dir );
|
105
libmui/src/mui/mui_control_group.h
Normal file
105
libmui/src/mui/mui_control_group.h
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* mui_control_group.h
|
||||
*
|
||||
* Copyright (C) 2024 Michel Pollet <buserror@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mui/mui_types.h>
|
||||
|
||||
struct mui_control_t;
|
||||
|
||||
/*
|
||||
* The control group is a a list of controls, the subtelety is that
|
||||
* there is a list of groups as well, so that controls can be grouped
|
||||
* together, and hidden or shown as a group.
|
||||
* This is mostly done to allow for 'tab' controls, where the controls
|
||||
* are grouped together, and only one group is shown at a time.
|
||||
* For most windows, there is only one group, and all controls are
|
||||
* in that group.
|
||||
* The 'hidden' flag is used to hide the whole group, and all the controls
|
||||
* in it.
|
||||
* This API allows iterating over all the controls in a group, or all
|
||||
* the controls in all the groups. The iterator allows to skip hidden
|
||||
* controls, or to walk all controls.
|
||||
*/
|
||||
struct mui_control_t;
|
||||
typedef struct mui_control_group_t {
|
||||
struct {
|
||||
uint hidden : 1;
|
||||
} flags;
|
||||
TAILQ_ENTRY(mui_control_group_t) self;
|
||||
TAILQ_HEAD(controls, mui_control_t) controls;
|
||||
} mui_control_group_t;
|
||||
|
||||
typedef struct mui_controls_t {
|
||||
TAILQ_HEAD(list, mui_control_group_t) controls;
|
||||
} mui_controls_t;
|
||||
|
||||
void
|
||||
mui_controls_init(
|
||||
mui_controls_t * group_list);
|
||||
// Return the 'current' group; this is more of a placeholder as it
|
||||
// always return the 'last' group in the list.
|
||||
struct mui_control_group_t *
|
||||
mui_controls_current_group(
|
||||
mui_controls_t * group_list);
|
||||
typedef enum mui_controls_flags_e {
|
||||
// only return visible controls
|
||||
MUI_CONTROLS_VISIBLE = 0,
|
||||
// return all controls, regardless of visibility
|
||||
MUI_CONTROLS_ALL = 1,
|
||||
} mui_controls_flags_e;
|
||||
|
||||
// Initializes a control group, optionaly (if 'attach' is not NULL) attach
|
||||
// it to the 'attach' group.
|
||||
void
|
||||
mui_control_group_init(
|
||||
struct mui_control_group_t * group,
|
||||
mui_controls_t * attach);
|
||||
struct mui_control_t *
|
||||
mui_control_group_first(
|
||||
struct mui_control_group_t * group,
|
||||
mui_controls_flags_e flags);
|
||||
struct mui_control_t *
|
||||
mui_control_group_last(
|
||||
struct mui_control_group_t * group,
|
||||
mui_controls_flags_e flags);
|
||||
struct mui_control_t *
|
||||
mui_control_group_next(
|
||||
struct mui_control_t * c,
|
||||
mui_controls_flags_e flags);
|
||||
struct mui_control_t *
|
||||
mui_control_group_prev(
|
||||
struct mui_control_t * c,
|
||||
mui_controls_flags_e flags);
|
||||
|
||||
// return first control in the list of groups.
|
||||
struct mui_control_t *
|
||||
mui_controls_first(
|
||||
mui_controls_t * group_list,
|
||||
mui_controls_flags_e flags);
|
||||
// return last control in the list of groups. Only return visible controls
|
||||
// if 'all' is false
|
||||
struct mui_control_t *
|
||||
mui_controls_last(
|
||||
mui_controls_t * group_list,
|
||||
mui_controls_flags_e flags);
|
||||
/*
|
||||
* Return the next control relative to 'c'; it can be in the same group
|
||||
* or any following groups.
|
||||
*/
|
||||
struct mui_control_t *
|
||||
mui_controls_next(
|
||||
struct mui_control_t * c,
|
||||
mui_controls_flags_e flags);
|
||||
/* Return the previous control relative to 'c'; it can be in the same group
|
||||
* or any previous groups.
|
||||
*/
|
||||
struct mui_control_t *
|
||||
mui_controls_prev(
|
||||
struct mui_control_t * control,
|
||||
mui_controls_flags_e flags);
|
195
libmui/src/mui/mui_drawable.h
Normal file
195
libmui/src/mui/mui_drawable.h
Normal file
@ -0,0 +1,195 @@
|
||||
/*
|
||||
* mui_drawable.h
|
||||
*
|
||||
* Copyright (C) 2024 Michel Pollet <buserror@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mui/mui_types.h>
|
||||
#include <pixman.h>
|
||||
|
||||
|
||||
struct cg_surface_t;
|
||||
struct cg_ctx_t;
|
||||
|
||||
/*
|
||||
* Describes a pixmap.
|
||||
* And really, only bpp:32 for ARGB is supported if you want to use 'cg' to draw
|
||||
* on it,
|
||||
* 8bpp is also used for alpha masks, in which case only the pixman API is used.
|
||||
* (Alpha mask is used for text rendering)
|
||||
*/
|
||||
typedef struct mui_pixmap_t {
|
||||
uint8_t * pixels;
|
||||
uint32_t bpp : 8;
|
||||
c2_pt_t size;
|
||||
uint32_t row_bytes;
|
||||
} mui_pixmap_t;
|
||||
|
||||
typedef pixman_region32_t mui_region_t;
|
||||
|
||||
DECLARE_C_ARRAY(mui_region_t, mui_clip_stack, 2);
|
||||
|
||||
/*
|
||||
██████ ██████ █████ ██ ██ █████ ██████ ██ ███████
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██████ ███████ ██ █ ██ ███████ ██████ ██ █████
|
||||
██ ██ ██ ██ ██ ██ ██ ███ ██ ██ ██ ██ ██ ██ ██
|
||||
██████ ██ ██ ██ ██ ███ ███ ██ ██ ██████ ███████ ███████
|
||||
*/
|
||||
/*
|
||||
* The Drawable is a drawing context. The important feature
|
||||
* of this is that it keeps a context for the pixman library destination
|
||||
* image, AND also the context for the 'cg' vectorial library.
|
||||
*
|
||||
* Furthermore it keeps track of a stack of clipping rectangles, and is able
|
||||
* to 'sync' the current clipping area for either (or both) cg and libpixman.
|
||||
*
|
||||
* Important note: the cg vectorial library coordinate system is placed on the
|
||||
* space *between* pixels, ie, if you moveto(1,1) and draw a line down, you
|
||||
* will light up pixels in columns zero AND one (at half transparency).
|
||||
* This differs significantly from for example, pixman that is uses pixel
|
||||
* coordinates on hard pixels.
|
||||
*
|
||||
* It's worth remembering as if you draw for example around the border of a
|
||||
* control, it will very likely be 'clipped' somewhat because half the pixels
|
||||
* are technically outside the control bounding/clipping rectangle.
|
||||
* You can easily adjust for this by adding 0.5 to the coordinates, if you
|
||||
* require it.
|
||||
*
|
||||
* Other imporant note: The clipping stack is only converted to pixman/cg when
|
||||
* the client code asks for the context. So you must make sure not to 'cache'
|
||||
* the context too early, otherwise the clipping won't work.
|
||||
* Bad:
|
||||
* struct cg_t * cg = mui_drawable_get_cg(dr);
|
||||
* mui_drawable_clip_push(dr, &r);
|
||||
* ...
|
||||
* mui_drawable_clip_pop(dr);
|
||||
* Good:
|
||||
* mui_drawable_clip_push(dr, &r);
|
||||
* struct cg_t * cg = mui_drawable_get_cg(dr);
|
||||
* ...
|
||||
* mui_drawable_clip_pop(dr);
|
||||
*/
|
||||
typedef struct mui_drawable_t {
|
||||
mui_pixmap_t pix; // *has* to be first in struct
|
||||
void * _pix_hash; // used to detect if pix has changed
|
||||
struct cg_surface_t * cg_surface;
|
||||
struct cg_ctx_t * cg; // Do not to use these directly
|
||||
union pixman_image * pixman; // Do not to use these directly
|
||||
uint pixman_clip_dirty: 1,
|
||||
cg_clip_dirty : 1,
|
||||
dispose_pixels : 1,
|
||||
dispose_drawable : 1;
|
||||
// not used internally, but useful for the application
|
||||
struct {
|
||||
float opacity;
|
||||
c2_pt_t size;
|
||||
uint id, kind;
|
||||
} texture;
|
||||
// (default) position in destination when drawing (optional)
|
||||
c2_pt_t origin;
|
||||
mui_clip_stack_t clip;
|
||||
} mui_drawable_t;
|
||||
|
||||
// Use IMPLEMENT_C_ARRAY(mui_drawable_array); if you need this
|
||||
DECLARE_C_ARRAY(mui_drawable_t *, mui_drawable_array, 4);
|
||||
|
||||
/*
|
||||
* Drawable related
|
||||
*/
|
||||
/* create a new mui_drawable of size w x h, bpp depth.
|
||||
* Optionally allocate the pixels if pixels is NULL. Allocated pixels
|
||||
* are not cleared to white/zero. */
|
||||
mui_drawable_t *
|
||||
mui_drawable_new(
|
||||
c2_pt_t size,
|
||||
uint8_t bpp,
|
||||
void * pixels, // if NULL, will allocate
|
||||
uint32_t row_bytes);
|
||||
/* initialize a mui_drawable_t structure with the given parameters
|
||||
* note it is not assumed 'd' contains anything valid, it will be
|
||||
* overwritten */
|
||||
mui_drawable_t *
|
||||
mui_drawable_init(
|
||||
mui_drawable_t * d,
|
||||
c2_pt_t size,
|
||||
uint8_t bpp,
|
||||
void * pixels, // if NULL, will allocate
|
||||
uint32_t row_bytes);
|
||||
void
|
||||
mui_drawable_dispose(
|
||||
mui_drawable_t * dr);
|
||||
// Clear, but do not dispose of the drawable
|
||||
void
|
||||
mui_drawable_clear(
|
||||
mui_drawable_t * dr);
|
||||
|
||||
// get/allocate a pixman structure for this drawable
|
||||
union pixman_image *
|
||||
mui_drawable_get_pixman(
|
||||
mui_drawable_t * dr);
|
||||
// get/allocate a cg drawing context for this
|
||||
struct cg_ctx_t *
|
||||
mui_drawable_get_cg(
|
||||
mui_drawable_t * dr);
|
||||
// return 0 (no intersect), 1: fully contained and 2: partial contains
|
||||
int
|
||||
mui_drawable_clip_intersects(
|
||||
mui_drawable_t * dr,
|
||||
c2_rect_p r );
|
||||
void
|
||||
mui_drawable_set_clip(
|
||||
mui_drawable_t * dr,
|
||||
c2_rect_array_p clip );
|
||||
int
|
||||
mui_drawable_clip_push(
|
||||
mui_drawable_t * dr,
|
||||
c2_rect_p r );
|
||||
int
|
||||
mui_drawable_clip_push_region(
|
||||
mui_drawable_t * dr,
|
||||
pixman_region32_t * rgn );
|
||||
int
|
||||
mui_drawable_clip_substract_region(
|
||||
mui_drawable_t * dr,
|
||||
pixman_region32_t * rgn );
|
||||
void
|
||||
mui_drawable_clip_pop(
|
||||
mui_drawable_t * dr );
|
||||
pixman_region32_t *
|
||||
mui_drawable_clip_get(
|
||||
mui_drawable_t * dr);
|
||||
|
||||
|
||||
/*
|
||||
* Your typical ARGB color. Note that the components are NOT
|
||||
* alpha-premultiplied at this stage.
|
||||
* This struct should be able to be passed as a value, not a pointer
|
||||
*/
|
||||
typedef union mui_color_t {
|
||||
struct {
|
||||
uint8_t a,r,g,b;
|
||||
} __attribute__((packed));
|
||||
uint32_t value;
|
||||
uint8_t v[4];
|
||||
} mui_color_t;
|
||||
|
||||
typedef struct mui_control_color_t {
|
||||
mui_color_t fill, frame, text;
|
||||
} mui_control_color_t;
|
||||
|
||||
#define MUI_COLOR(_v) ((mui_color_t){ .value = (_v)})
|
||||
|
||||
#define CG_COLOR(_c) (struct cg_color_t){ \
|
||||
.a = (_c).a / 255.0, .r = (_c).r / 255.0, \
|
||||
.g = (_c).g / 255.0, .b = (_c).b / 255.0 }
|
||||
/*
|
||||
* Pixman use premultiplied alpha values
|
||||
*/
|
||||
#define PIXMAN_COLOR(_c) (pixman_color_t){ \
|
||||
.alpha = (_c).a * 257, .red = (_c).r * (_c).a, \
|
||||
.green = (_c).g * (_c).a, .blue = (_c).b * (_c).a }
|
72
libmui/src/mui/mui_event.h
Normal file
72
libmui/src/mui/mui_event.h
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* mui_event.h
|
||||
*
|
||||
* Copyright (C) 2024 Michel Pollet <buserror@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mui/mui_types.h>
|
||||
#include <mui/mui_timer.h>
|
||||
|
||||
|
||||
/*
|
||||
* Event description. pretty standard stuff here -- the 'when' field is
|
||||
* only used really to detect double clicks so far.
|
||||
*
|
||||
* Even handlers should return true if the event was handled, (in which case
|
||||
* even processing stops for that event) or false to continue passing the even
|
||||
* down the chain.
|
||||
*
|
||||
* Events are passed to the top window first, and then down the chain of
|
||||
* windows, until one of them returns true.
|
||||
* Implicitely, it means the menubar gets to see the events first, even clicks,
|
||||
* even if the click wasn't in the menubar. This is also true of key events of
|
||||
* course, which allows the menu to detect key combos, first.
|
||||
*/
|
||||
typedef struct mui_event_t {
|
||||
mui_event_e type;
|
||||
mui_time_t when;
|
||||
mui_modifier_e modifiers;
|
||||
union {
|
||||
struct key {
|
||||
uint32_t key; // ASCII or mui_key_e
|
||||
bool up;
|
||||
} key;
|
||||
struct {
|
||||
uint32_t button : 4,
|
||||
count : 2; // click count
|
||||
c2_pt_t where;
|
||||
} mouse;
|
||||
struct {
|
||||
int32_t delta;
|
||||
c2_pt_t where;
|
||||
} wheel;
|
||||
struct { // MUI_EVENT_TEXT is of variable size!
|
||||
uint32_t size;
|
||||
uint8_t text[0];
|
||||
} text;
|
||||
};
|
||||
} mui_event_t;
|
||||
|
||||
/* Just a generic buffer for UTF8 text */
|
||||
DECLARE_C_ARRAY(uint8_t, mui_utf8, 8);
|
||||
IMPLEMENT_C_ARRAY(mui_utf8);
|
||||
|
||||
/*
|
||||
* Key equivalent, used to match key events to menu items
|
||||
* Might be extended to controls, right now only the 'key' is checked,
|
||||
* mostly for Return and ESC.
|
||||
*/
|
||||
typedef union mui_key_equ_t {
|
||||
struct {
|
||||
uint16_t mod;
|
||||
uint16_t key;
|
||||
};
|
||||
uint32_t value;
|
||||
} mui_key_equ_t;
|
||||
|
||||
#define MUI_KEY_EQU(_mask, _key) \
|
||||
(mui_key_equ_t){ .mod = (_mask), .key = (_key) }
|
103
libmui/src/mui/mui_menu.h
Normal file
103
libmui/src/mui/mui_menu.h
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* mui_menu.h
|
||||
*
|
||||
* Copyright (C) 2024 Michel Pollet <buserror@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mui/mui_types.h>
|
||||
#include <mui/mui_event.h>
|
||||
#include <mui/mui_window.h>
|
||||
|
||||
struct mui_menu_items_t;
|
||||
|
||||
/*
|
||||
* This is a menu item descriptor (also used for the titles, bar a few bits).
|
||||
* This is not a *control* in the window, instead this is used to describe
|
||||
* the menus and menu item controls that are created when the menu becomes
|
||||
* visible.
|
||||
*/
|
||||
typedef struct mui_menu_item_t {
|
||||
uint32_t disabled : 1,
|
||||
hilited : 1,
|
||||
is_menutitle : 1;
|
||||
uint32_t index: 9;
|
||||
uint32_t uid;
|
||||
char * title;
|
||||
// currently only supported for menu titles
|
||||
const uint32_t * color_icon; // optional, ARGB colors
|
||||
char mark[8]; // UTF8 -- Charcoal
|
||||
char icon[8]; // UTF8 -- Wider, icon font
|
||||
char kcombo[16]; // UTF8 -- display only
|
||||
mui_key_equ_t key_equ; // keystroke to select this item
|
||||
struct mui_menu_item_t * submenu;
|
||||
c2_coord_t location; // calculated by menu creation code
|
||||
c2_coord_t height;
|
||||
} mui_menu_item_t;
|
||||
|
||||
/*
|
||||
* The menu item array is atypical as the items ('e' field) are not allocated
|
||||
* by the array, but by the menu creation code. This is because the menu
|
||||
* reuses the pointer to the items that is passed when the menu is added to
|
||||
* the menubar.
|
||||
* the 'read only' field is used to prevent the array from trying to free the
|
||||
* items when being disposed.
|
||||
*/
|
||||
DECLARE_C_ARRAY(mui_menu_item_t, mui_menu_items, 2,
|
||||
bool read_only; );
|
||||
IMPLEMENT_C_ARRAY(mui_menu_items);
|
||||
|
||||
enum mui_menubar_action_e {
|
||||
// parameter is a mui_menu_item_t* for the first item of the menu,
|
||||
// this is exactly the parameter passed to add_simple()
|
||||
// you can use this to disable/enable menu items etc
|
||||
MUI_MENUBAR_ACTION_PREPARE = FCC('m','b','p','r'),
|
||||
// parameter 'target' is a mui_menuitem_t*
|
||||
MUI_MENUBAR_ACTION_SELECT = FCC('m','b','a','r'),
|
||||
};
|
||||
/*
|
||||
* Menu related.
|
||||
* Menubar, and menus/popups are windows as well, in a layer above the
|
||||
* normal ones.
|
||||
*/
|
||||
mui_window_t *
|
||||
mui_menubar_new(
|
||||
struct mui_t * ui );
|
||||
// return the previously created menubar (or NULL)
|
||||
mui_window_t *
|
||||
mui_menubar_get(
|
||||
struct mui_t * ui );
|
||||
|
||||
/*
|
||||
* Add a menu to the menubar. 'items' is an array of mui_menu_item_t
|
||||
* terminated by an element with a NULL title.
|
||||
*
|
||||
* Note: The array is NOT const, it will be tweaked for storing items
|
||||
* position, it can also be tweaked to set/reset the disabled state,
|
||||
* check marks etc
|
||||
*
|
||||
* Once created, you can do a mui_popupmenu_get_items() to get the array,
|
||||
* modify it (still make sure there is a NULL item at the end) then
|
||||
* call mui_popupmenu_prepare() to update the menu.
|
||||
*/
|
||||
struct mui_control_t *
|
||||
mui_menubar_add_simple(
|
||||
mui_window_t * win,
|
||||
const char * title,
|
||||
uint32_t menu_uid,
|
||||
mui_menu_item_t * items );
|
||||
struct mui_control_t *
|
||||
mui_menubar_add_menu(
|
||||
mui_window_t * win,
|
||||
uint32_t menu_uid,
|
||||
mui_menu_item_t * items,
|
||||
uint count );
|
||||
|
||||
/* Turn off any highlighted menu titles */
|
||||
mui_window_t *
|
||||
mui_menubar_highlight(
|
||||
mui_window_t * win,
|
||||
bool ignored );
|
95
libmui/src/mui/mui_ref.h
Normal file
95
libmui/src/mui/mui_ref.h
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* mui_ref.h
|
||||
*
|
||||
* Copyright (C) 2024 Michel Pollet <buserror@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mui/mui_types.h>
|
||||
|
||||
struct mui_t;
|
||||
struct mui_control_t;
|
||||
struct mui_window_t;
|
||||
|
||||
/*!
|
||||
* References allows arbitrary code to keep a 'handle' on either
|
||||
* a window or a control. This is used for example to keep track of
|
||||
* the currently focused control.
|
||||
* Client code Must Not keep random pointers on control and windows,
|
||||
* as they could get deleted and they will end up with a dangling
|
||||
* pointer.
|
||||
* Instead, client code create a reference, and use that reference
|
||||
* to keep track of the object. If an object is deleted, all it's
|
||||
* current references are reset to NULL, so the client code can
|
||||
* detect that the object is gone just by checking that its pointer
|
||||
* is still around. Otherwise, it's just gone.
|
||||
*/
|
||||
struct mui_ref_t;
|
||||
|
||||
typedef struct mui_refqueue_t {
|
||||
TAILQ_HEAD(head, mui_ref_t) head;
|
||||
} mui_refqueue_t;
|
||||
|
||||
typedef void (*mui_deref_p)(
|
||||
struct mui_ref_t * ref);
|
||||
|
||||
typedef struct mui_ref_t {
|
||||
// in refqueue's 'head'
|
||||
TAILQ_ENTRY(mui_ref_t) self;
|
||||
mui_refqueue_t * queue;
|
||||
// OPTIONAL arbitrary kind set when referencing an object.
|
||||
uint32_t kind;
|
||||
uint32_t alloc : 1, trace : 1, count : 8;
|
||||
// OPTIONAL: called if the object win/control get disposed or
|
||||
// otherwise dereferenced.
|
||||
mui_deref_p deref;
|
||||
} _mui_ref_t; // this is not a 'user' type.
|
||||
|
||||
/*
|
||||
* Window and Control references
|
||||
* While these two count technically be a union, I've deciced for separate
|
||||
* types to enforce the type checking.
|
||||
*/
|
||||
typedef struct mui_window_ref_t {
|
||||
_mui_ref_t ref;
|
||||
struct mui_window_t * window;
|
||||
} mui_window_ref_t;
|
||||
|
||||
typedef struct mui_control_ref_t {
|
||||
_mui_ref_t ref;
|
||||
struct mui_control_t * control;
|
||||
} mui_control_ref_t;
|
||||
|
||||
/*!
|
||||
* Initializes a reference to 'control', with the (optional) kind.
|
||||
* if 'ref' is NULL a new reference is allocated and returned, will be
|
||||
* freed on deref().
|
||||
* 'kind' is an optional arbitrary value that can be used to identify
|
||||
* the reference, it has no meaning to the library.
|
||||
*/
|
||||
mui_control_ref_t *
|
||||
mui_control_ref(
|
||||
mui_control_ref_t * ref,
|
||||
struct mui_control_t * control,
|
||||
uint32_t kind);
|
||||
void
|
||||
mui_control_deref(
|
||||
mui_control_ref_t * ref);
|
||||
/*!
|
||||
* Initializes a reference to 'window', with the (optional) kind.
|
||||
* if 'ref' is NULL a new reference is allocated and returned, will be
|
||||
* freed on deref().
|
||||
* 'kind' is an optional arbitrary value that can be used to identify
|
||||
* the reference, it has no meaning to the library.
|
||||
*/
|
||||
mui_window_ref_t *
|
||||
mui_window_ref(
|
||||
mui_window_ref_t * ref,
|
||||
struct mui_window_t * win,
|
||||
uint32_t kind);
|
||||
void
|
||||
mui_window_deref(
|
||||
mui_window_ref_t * ref);
|
73
libmui/src/mui/mui_stdfile.h
Normal file
73
libmui/src/mui/mui_stdfile.h
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* mui_stdfile.h
|
||||
*
|
||||
* Copyright (C) 2024 Michel Pollet <buserror@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mui/mui_types.h>
|
||||
#include <mui/mui_window.h>
|
||||
|
||||
|
||||
/*
|
||||
* Standard file dialog
|
||||
*/
|
||||
enum mui_std_action_e {
|
||||
MUI_STDF_ACTION_NONE = 0,
|
||||
// parameter 'target' is a char * with full pathname of selected file
|
||||
MUI_STDF_ACTION_SELECT = FCC('s','t','d','s'),
|
||||
MUI_STDF_ACTION_CANCEL = FCC('s','t','d','c'),
|
||||
};
|
||||
enum mui_std_flags_e {
|
||||
// 'pattern' is a GNU extended regexp applied to filenames.
|
||||
MUI_STDF_FLAG_REGEXP = (1 << 0),
|
||||
// don't use the 'pref_directory', load, or same preference files
|
||||
MUI_STDF_FLAG_NOPREF = (1 << 1),
|
||||
};
|
||||
|
||||
/*
|
||||
* Standard file dialog related
|
||||
*
|
||||
* Presents a standard 'get' file dialog, with optional prompt, regexp and
|
||||
* start path. The return value is a pointer to a window, you can add your own
|
||||
* 'action' function to get MUI_STDF_ACTION_* events.
|
||||
* Once in the action function, you can call mui_stdfile_get_selected_path()
|
||||
* to get the selected path, and free it when done.
|
||||
* NOTE: The dialog does not auto-close, your own action function should close
|
||||
* the dialog using mui_window_dispose().
|
||||
*
|
||||
* The dialog will attempt to remember the last directory used *for this
|
||||
* particular pattern* and will use it as the default start path when called
|
||||
* again. This is optional, it requires a mui->pref_directory to be set.
|
||||
* You can also disable this feature by setting the MUI_STDF_FLAG_NOPREF flag.
|
||||
*
|
||||
* + 'pattern' is a regular expression to filter the files, or NULL for no
|
||||
* filter.
|
||||
* + if 'start_path' is NULL, the $HOME directory is used.
|
||||
* + 'where' is the location of the dialog, (0,0) will center it.
|
||||
*/
|
||||
mui_window_t *
|
||||
mui_stdfile_get(
|
||||
struct mui_t * ui,
|
||||
c2_pt_t where,
|
||||
const char * prompt,
|
||||
const char * pattern,
|
||||
const char * start_path,
|
||||
uint16_t flags );
|
||||
// return the curently selected pathname -- caller must free() it
|
||||
char *
|
||||
mui_stdfile_get_selected_path(
|
||||
mui_window_t * w );
|
||||
|
||||
mui_window_t *
|
||||
mui_stdfile_put(
|
||||
struct mui_t * ui,
|
||||
c2_pt_t where, // pass 0,0 to center
|
||||
const char * prompt, // Window title
|
||||
const char * pattern, // Enforce any of these suffixes
|
||||
const char * start_path, // start in this path (optional)
|
||||
const char * save_filename, // start with this filename
|
||||
uint16_t flags );
|
159
libmui/src/mui/mui_text.h
Normal file
159
libmui/src/mui/mui_text.h
Normal file
@ -0,0 +1,159 @@
|
||||
/*
|
||||
* mui_text.h
|
||||
*
|
||||
* Copyright (C) 2024 Michel Pollet <buserror@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mui/mui_types.h>
|
||||
#include <mui/mui_drawable.h>
|
||||
|
||||
typedef struct mui_font_t {
|
||||
// this MUST be first
|
||||
mui_drawable_t font; // points to ttc pixels!
|
||||
char * name; // not filename, internal name, aka 'main'
|
||||
uint size; // in pixels
|
||||
TAILQ_ENTRY(mui_font_t) self;
|
||||
struct stb_ttc_info ttc; // internal; glyph cache/hash etc
|
||||
} mui_font_t;
|
||||
|
||||
/*
|
||||
* Font related
|
||||
*/
|
||||
void
|
||||
mui_font_init(
|
||||
struct mui_t * ui);
|
||||
void
|
||||
mui_font_dispose(
|
||||
struct mui_t * ui);
|
||||
|
||||
mui_font_t *
|
||||
mui_font_find(
|
||||
struct mui_t * ui,
|
||||
const char * name);
|
||||
mui_font_t *
|
||||
mui_font_from_mem(
|
||||
struct mui_t * ui,
|
||||
const char * name,
|
||||
uint size,
|
||||
const void * font_data,
|
||||
uint font_size );
|
||||
/*
|
||||
* Draw a text string at 'where' in the drawable 'dr' with the
|
||||
* given color. This doesn't handle line wrapping, or anything,
|
||||
* it just draws the text at the given position.
|
||||
* If you want more fancy text drawing, use mui_font_textbox()
|
||||
*/
|
||||
void
|
||||
mui_font_text_draw(
|
||||
mui_font_t * font,
|
||||
mui_drawable_t *dr,
|
||||
c2_pt_t where,
|
||||
const char * text,
|
||||
uint text_len,
|
||||
mui_color_t color);
|
||||
/*
|
||||
* This is a low level function to measure a text string, it returns
|
||||
* the width of the string in pixels, and fills the 'm' structure with
|
||||
* the position of each glyph in the string. Note that the returned
|
||||
* values are all in FIXED POINT format.
|
||||
*/
|
||||
int
|
||||
mui_font_text_measure(
|
||||
mui_font_t * font,
|
||||
const char * text,
|
||||
struct stb_ttc_measure *m );
|
||||
|
||||
typedef enum mui_text_e {
|
||||
// 2 bits for horizontal alignment, 2 bits for vertical alignment
|
||||
MUI_TEXT_ALIGN_LEFT = 0,
|
||||
MUI_TEXT_ALIGN_CENTER = (1 << 0),
|
||||
MUI_TEXT_ALIGN_RIGHT = (1 << 1),
|
||||
MUI_TEXT_ALIGN_TOP = 0,
|
||||
MUI_TEXT_ALIGN_MIDDLE = (MUI_TEXT_ALIGN_CENTER << 2),
|
||||
MUI_TEXT_ALIGN_BOTTOM = (MUI_TEXT_ALIGN_RIGHT << 2),
|
||||
MUI_TEXT_ALIGN_FULL = (1 << 5),
|
||||
MUI_TEXT_ALIGN_COMPACT = (1 << 6), // compact line spacing
|
||||
MUI_TEXT_DEBUG = (1 << 7),
|
||||
MUI_TEXT_STYLE_BOLD = (1 << 8), // Synthetic (ugly) bold
|
||||
MUI_TEXT_STYLE_ULINE = (1 << 9), // Underline
|
||||
MUI_TEXT_STYLE_NARROW = (1 << 10),// Synthetic narrow
|
||||
MUI_TEXT_FLAGS_COUNT = 11,
|
||||
} mui_text_e;
|
||||
|
||||
/*
|
||||
* Draw a text string in a bounding box, with the given color. The
|
||||
* 'flags' parameter is a combination of MUI_TEXT_ALIGN_* flags.
|
||||
* This function will handle line wrapping, and will draw as much text
|
||||
* as it can in the given box.
|
||||
* The 'text' parameter can be a UTF8 string, and the 'text_len' is
|
||||
* the number of bytes in the string (or zero), not the number of
|
||||
* glyphs.
|
||||
* The 'text' string can contain '\n' to force a line break.
|
||||
*/
|
||||
void
|
||||
mui_font_textbox(
|
||||
mui_font_t * font,
|
||||
mui_drawable_t *dr,
|
||||
c2_rect_t bbox,
|
||||
const char * text,
|
||||
uint text_len,
|
||||
mui_color_t color,
|
||||
mui_text_e flags );
|
||||
|
||||
// this is what is returned by mui_font_measure()
|
||||
typedef struct mui_glyph_t {
|
||||
uint32_t pos; // position in text, in *bytes*
|
||||
uint32_t w; // width of the glyph, in *pixels*
|
||||
float x; // x position in *pixels*
|
||||
uint32_t index; // cache index, for internal use, do not change
|
||||
uint32_t glyph; // Unicode codepoint
|
||||
} mui_glyph_t;
|
||||
|
||||
DECLARE_C_ARRAY(mui_glyph_t, mui_glyph_array, 8,
|
||||
uint line_break : 1;
|
||||
int x, y, t, b; float w;);
|
||||
DECLARE_C_ARRAY(mui_glyph_array_t, mui_glyph_line_array, 8,
|
||||
uint margin_left, margin_right, // minimum x, and max width
|
||||
height; );
|
||||
|
||||
/*
|
||||
* Measure a text string, return the number of lines, and each glyphs
|
||||
* position already aligned to the MUI_TEXT_ALIGN_* flags.
|
||||
* Note that the 'compact', 'narrow' flags are used here,
|
||||
* the 'compact' flag is used to reduce the line spacing, and the
|
||||
* 'narrow' flag is used to reduce the advance between glyphs.
|
||||
*/
|
||||
void
|
||||
mui_font_measure(
|
||||
mui_font_t * font,
|
||||
c2_rect_t bbox,
|
||||
const char * text,
|
||||
uint text_len,
|
||||
mui_glyph_line_array_t *lines,
|
||||
mui_text_e flags);
|
||||
/*
|
||||
* to be used exclusively with mui_font_measure.
|
||||
* Draw the lines and glyphs returned by mui_font_measure, with the
|
||||
* given color and flags.
|
||||
* The significant flags here are no longer the text aligment, but
|
||||
* how to render them:
|
||||
* + MUI_TEXT_STYLE_BOLD will draw each glyphs twice, offset by 1 pixel
|
||||
* + MUI_TEXT_STYLE_ULINE will draw a line under the text glyphs, unless
|
||||
* they have a descent that is lower than the underline.
|
||||
*/
|
||||
void
|
||||
mui_font_measure_draw(
|
||||
mui_font_t * font,
|
||||
mui_drawable_t *dr,
|
||||
c2_rect_t bbox,
|
||||
mui_glyph_line_array_t *lines,
|
||||
mui_color_t color,
|
||||
mui_text_e flags);
|
||||
// clear all the lines, and glyph lists. Use it after mui_font_measure
|
||||
void
|
||||
mui_font_measure_clear(
|
||||
mui_glyph_line_array_t *lines);
|
76
libmui/src/mui/mui_timer.h
Normal file
76
libmui/src/mui/mui_timer.h
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* mui_timer.h
|
||||
*
|
||||
* Copyright (C) 2024 Michel Pollet <buserror@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mui/mui_types.h>
|
||||
|
||||
|
||||
typedef uint64_t mui_time_t;
|
||||
|
||||
struct mui_t;
|
||||
|
||||
/*!
|
||||
* Timer callback definition. Behaves in a pretty standard way; the timer
|
||||
* returns 0 to be cancelled (for one shot timers for example) or return
|
||||
* the delay to the next call (that will be added to 'now' to get the next)
|
||||
*/
|
||||
typedef mui_time_t (*mui_timer_p)(
|
||||
struct mui_t * mui,
|
||||
mui_time_t now,
|
||||
void * param);
|
||||
|
||||
|
||||
enum mui_time_e {
|
||||
MUI_TIME_RES = 1,
|
||||
MUI_TIME_SECOND = 1000000,
|
||||
MUI_TIME_MS = (MUI_TIME_SECOND/1000),
|
||||
};
|
||||
mui_time_t
|
||||
mui_get_time();
|
||||
|
||||
#define MUI_TIMER_COUNT 64
|
||||
#define MUI_TIMER_NONE 0xff
|
||||
|
||||
typedef uint8_t mui_timer_id_t;
|
||||
|
||||
typedef struct mui_timer_group_t {
|
||||
uint64_t map;
|
||||
struct {
|
||||
mui_time_t when;
|
||||
mui_timer_p cb;
|
||||
void * param;
|
||||
} timers[MUI_TIMER_COUNT];
|
||||
} mui_timer_group_t;
|
||||
|
||||
/*
|
||||
* Register 'cb' to be called after 'delay'. Returns a timer id (0 to 63)
|
||||
* or MUI_TIMER_NONE if no timer is available.
|
||||
* The timer function cb can return 0 for a one shot timer, or another
|
||||
* delay that will be added to the current stamp for a further call
|
||||
* of the timer.
|
||||
* 'param' will be also passed to the timer callback.
|
||||
*/
|
||||
mui_timer_id_t
|
||||
mui_timer_register(
|
||||
struct mui_t * ui,
|
||||
mui_timer_p cb,
|
||||
void * param,
|
||||
uint32_t delay);
|
||||
/*
|
||||
* Reset timer 'id' if 'cb' matches what was registered. Set a new delay,
|
||||
* or cancel the timer if delay is 0.
|
||||
* Returns the time that was left on the timer, or 0 if the timer was
|
||||
* not found.
|
||||
*/
|
||||
mui_time_t
|
||||
mui_timer_reset(
|
||||
struct mui_t * ui,
|
||||
mui_timer_id_t id,
|
||||
mui_timer_p cb,
|
||||
mui_time_t delay);
|
158
libmui/src/mui/mui_types.h
Normal file
158
libmui/src/mui/mui_types.h
Normal file
@ -0,0 +1,158 @@
|
||||
/*
|
||||
* mui_types.h
|
||||
*
|
||||
* Copyright (C) 2024 Michel Pollet <buserror@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "c2_arrays.h"
|
||||
|
||||
#ifdef __wasm__
|
||||
typedef unsigned int uint;
|
||||
#endif
|
||||
|
||||
#if 0 // only use to debug queue macros; do not enable
|
||||
#define _KERNEL
|
||||
#define INVARIANTS
|
||||
#define QUEUE_MACRO_DEBUG_TRACE
|
||||
#define panic(...) \
|
||||
do { \
|
||||
fprintf(stderr, "PANIC: %s:%d\n", __func__, __LINE__); \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
*((int*)0) = 0xdead; \
|
||||
} while(0)
|
||||
#endif
|
||||
|
||||
#include "bsd_queue.h"
|
||||
#include "stb_ttc.h"
|
||||
|
||||
/* Four Character Constants are used everywhere. Wish this had become a standard,
|
||||
* as it is so handy -- but nope, thus the macro. Annoyingly, the little-
|
||||
* endianess of them makes it a pain to do a printf() with them, this is why
|
||||
* the values are reversed here.
|
||||
*/
|
||||
#include <ctype.h>
|
||||
#define FCC(_a,_b,_c,_d) (((_d)<<24)|((_c)<<16)|((_b)<<8)|(_a))
|
||||
/* These are made to allow FCC to have a numerical index, this is
|
||||
* mostly used for radio button, menu items and so on */
|
||||
#define FCC_MASK FCC(0xff,0xff,0xff,0)
|
||||
/* number of bits to shift to get the fourth character of _fcc */
|
||||
#define FCC_SHIFT(_fcc) ((_fcc)>>(ffs(~FCC_MASK)-1) & 0xff)
|
||||
/* extract the index number of a fcc of type abcX where X is '0' - '9' */
|
||||
#define FCC_INDEX(_fcc) (isdigit(FCC_SHIFT(_fcc)) ? \
|
||||
((FCC_SHIFT(_fcc)) - '0') : 0)
|
||||
#define FCC_INDEXED(_fcc, _idx) \
|
||||
((_fcc & FCC_MASK) | ('0'+((_idx) & 0xff)) << (ffs(~FCC_MASK)-1))
|
||||
|
||||
typedef enum mui_event_e {
|
||||
MUI_EVENT_KEYUP = 0,
|
||||
MUI_EVENT_KEYDOWN,
|
||||
MUI_EVENT_BUTTONUP,
|
||||
MUI_EVENT_BUTTONDOWN,
|
||||
MUI_EVENT_BUTTONDBL, // double click
|
||||
MUI_EVENT_WHEEL,
|
||||
MUI_EVENT_DRAG,
|
||||
// the following ones aren't supported yet
|
||||
MUI_EVENT_TEXT, // UTF8 sequence [TODO]
|
||||
MUI_EVENT_MOUSEENTER,
|
||||
MUI_EVENT_MOUSELEAVE,
|
||||
MUI_EVENT_RESIZE,
|
||||
MUI_EVENT_CLOSE,
|
||||
|
||||
MUI_EVENT_COUNT,
|
||||
// left, middle, right buttons for clicks
|
||||
MUI_EVENT_BUTTON_MAX = 3,
|
||||
} mui_event_e;
|
||||
|
||||
typedef enum mui_key_e {
|
||||
// these are ASCII
|
||||
MUI_KEY_ESCAPE = 0x1b,
|
||||
MUI_KEY_SPACE = 0x20,
|
||||
MUI_KEY_RETURN = 0x0d,
|
||||
MUI_KEY_TAB = 0x09,
|
||||
MUI_KEY_BACKSPACE = 0x08,
|
||||
// these are not ASCII
|
||||
MUI_KEY_LEFT = 0x80, MUI_KEY_UP, MUI_KEY_RIGHT, MUI_KEY_DOWN,
|
||||
MUI_KEY_INSERT, MUI_KEY_DELETE,
|
||||
MUI_KEY_HOME, MUI_KEY_END,
|
||||
MUI_KEY_PAGEUP, MUI_KEY_PAGEDOWN,
|
||||
MUI_KEY_MODIFIERS = 0x90,
|
||||
MUI_KEY_LSHIFT = MUI_KEY_MODIFIERS,
|
||||
MUI_KEY_RSHIFT,
|
||||
MUI_KEY_LCTRL, MUI_KEY_RCTRL,
|
||||
MUI_KEY_LALT, MUI_KEY_RALT,
|
||||
MUI_KEY_LSUPER, MUI_KEY_RSUPER,
|
||||
MUI_KEY_CAPSLOCK,
|
||||
MUI_KEY_MODIFIERS_LAST,
|
||||
MUI_KEY_F1 = 0x100, MUI_KEY_F2, MUI_KEY_F3, MUI_KEY_F4,
|
||||
MUI_KEY_F5, MUI_KEY_F6, MUI_KEY_F7, MUI_KEY_F8,
|
||||
MUI_KEY_F9, MUI_KEY_F10,MUI_KEY_F11,MUI_KEY_F12,
|
||||
} mui_key_e;
|
||||
|
||||
typedef enum mui_modifier_e {
|
||||
MUI_MODIFIER_LSHIFT = (1 << (MUI_KEY_LSHIFT - MUI_KEY_MODIFIERS)),
|
||||
MUI_MODIFIER_RSHIFT = (1 << (MUI_KEY_RSHIFT - MUI_KEY_MODIFIERS)),
|
||||
MUI_MODIFIER_LCTRL = (1 << (MUI_KEY_LCTRL - MUI_KEY_MODIFIERS)),
|
||||
MUI_MODIFIER_RCTRL = (1 << (MUI_KEY_RCTRL - MUI_KEY_MODIFIERS)),
|
||||
MUI_MODIFIER_LALT = (1 << (MUI_KEY_LALT - MUI_KEY_MODIFIERS)),
|
||||
MUI_MODIFIER_RALT = (1 << (MUI_KEY_RALT - MUI_KEY_MODIFIERS)),
|
||||
MUI_MODIFIER_RSUPER = (1 << (MUI_KEY_RSUPER - MUI_KEY_MODIFIERS)),
|
||||
MUI_MODIFIER_LSUPER = (1 << (MUI_KEY_LSUPER - MUI_KEY_MODIFIERS)),
|
||||
|
||||
// special flag, trace events handling for this event
|
||||
MUI_MODIFIER_EVENT_TRACE= (1 << 15),
|
||||
MUI_MODIFIER_SHIFT = (MUI_MODIFIER_LSHIFT | MUI_MODIFIER_RSHIFT),
|
||||
MUI_MODIFIER_CTRL = (MUI_MODIFIER_LCTRL | MUI_MODIFIER_RCTRL),
|
||||
MUI_MODIFIER_ALT = (MUI_MODIFIER_LALT | MUI_MODIFIER_RALT),
|
||||
MUI_MODIFIER_SUPER = (MUI_MODIFIER_LSUPER | MUI_MODIFIER_RSUPER),
|
||||
} mui_modifier_e;
|
||||
|
||||
/*
|
||||
* The following constants are in UTF8 format, and relate to glyphs in
|
||||
* the TTF fonts
|
||||
*/
|
||||
/* These are from the icon font */
|
||||
#define MUI_ICON_FOLDER ""
|
||||
#define MUI_ICON_FOLDER_OPEN ""
|
||||
#define MUI_ICON_ROOT ""
|
||||
#define MUI_ICON_FILE ""
|
||||
#define MUI_ICON_POPUP_ARROWS ""
|
||||
#define MUI_ICON_HOME ""
|
||||
#define MUI_ICON_SBAR_UP ""
|
||||
#define MUI_ICON_SBAR_DOWN ""
|
||||
#define MUI_ICON_FLOPPY5 ""
|
||||
#define MUI_ICON_HARDDISK ""
|
||||
|
||||
/* These are specific to our custom version of the Charcoal System font */
|
||||
#define MUI_GLYPH_APPLE "" // solid apple
|
||||
#define MUI_GLYPH_OAPPLE "" // open apple
|
||||
#define MUI_GLYPH_COMMAND ""
|
||||
#define MUI_GLYPH_OPTION ""
|
||||
#define MUI_GLYPH_CONTROL ""
|
||||
#define MUI_GLYPH_SHIFT ""
|
||||
#define MUI_GLYPH_TICK "" // tickmark for menus
|
||||
#define MUI_GLYPH_SUBMENU "▶" // custom, for the hierarchical menus
|
||||
#define MUI_GLYPH_IIE "" // custom, IIe glyph
|
||||
#define MUI_GLYPH_POPMARK "▼" // custom, popup menu marker
|
||||
#define MUI_GLYPH_CLOSEBOX "☐" // custom, close box
|
||||
#define MUI_GLYPH_CLICKBOX "☒" // custom, clicked (close, zoom?) box
|
||||
/* These are also from Charcoal System font (added to the original) */
|
||||
#define MUI_GLYPH_F1 ""
|
||||
#define MUI_GLYPH_F2 ""
|
||||
#define MUI_GLYPH_F3 ""
|
||||
#define MUI_GLYPH_F4 ""
|
||||
#define MUI_GLYPH_F5 ""
|
||||
#define MUI_GLYPH_F6 ""
|
||||
#define MUI_GLYPH_F7 ""
|
||||
#define MUI_GLYPH_F8 ""
|
||||
#define MUI_GLYPH_F9 ""
|
||||
#define MUI_GLYPH_F10 ""
|
||||
#define MUI_GLYPH_F11 ""
|
||||
#define MUI_GLYPH_F12 ""
|
||||
|
||||
|
186
libmui/src/mui/mui_window.h
Normal file
186
libmui/src/mui/mui_window.h
Normal file
@ -0,0 +1,186 @@
|
||||
/*
|
||||
* mui_window.h
|
||||
*
|
||||
* Copyright (C) 2024 Michel Pollet <buserror@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mui/mui_types.h>
|
||||
#include <mui/mui_control.h>
|
||||
#include <mui/mui_drawable.h>
|
||||
#include <mui/mui_control_group.h>
|
||||
#include <mui/mui_action.h>
|
||||
#include <mui/mui_ref.h>
|
||||
|
||||
|
||||
/*
|
||||
* Window DEFinition -- Handle all related to a window, from drawing to
|
||||
* event handling.
|
||||
*/
|
||||
enum {
|
||||
MUI_WDEF_INIT = 0, // param is NULL
|
||||
MUI_WDEF_DISPOSE, // param is NULL
|
||||
MUI_WDEF_DRAW, // param is mui_drawable_t*
|
||||
MUI_WDEF_EVENT, // param is mui_event_t*
|
||||
MUI_WDEF_SELECT, // param is NULL
|
||||
MUI_WDEF_DESELECT, // param is NULL
|
||||
};
|
||||
typedef bool (*mui_wdef_p)(
|
||||
struct mui_window_t * win,
|
||||
uint8_t what,
|
||||
void * param);
|
||||
|
||||
enum mui_window_layer_e {
|
||||
MUI_WINDOW_LAYER_NORMAL = 0,
|
||||
MUI_WINDOW_LAYER_MODAL = 3,
|
||||
MUI_WINDOW_LAYER_ALERT = 5,
|
||||
MUI_WINDOW_LAYER_TOP = 15,
|
||||
MUI_WINDOW_LAYER_MASK = 0x0F, // mask layer bits
|
||||
// Menubar and Menus (popups) are also windows
|
||||
MUI_WINDOW_MENUBAR_LAYER = MUI_WINDOW_LAYER_TOP - 1,
|
||||
MUI_WINDOW_MENU_LAYER,
|
||||
|
||||
MUI_WINDOW_FLAGS_CLOSEBOX = 0x100,
|
||||
};
|
||||
|
||||
enum mui_window_action_e {
|
||||
MUI_WINDOW_ACTION_NONE = 0,
|
||||
// window is closing. param is NULL
|
||||
MUI_WINDOW_ACTION_CLOSE = FCC('w','c','l','s'),
|
||||
// closebox has been clicked. param is a bool* with 'true' (default) if
|
||||
// the window should be closed. 'false' if it shouldn't be close now
|
||||
MUI_WINDOW_ACTION_CLOSEBOX = FCC('w','c','b','x'),
|
||||
};
|
||||
|
||||
/*
|
||||
* A window is basically 2 rectangles in 'screen' coordinates. The
|
||||
* 'frame' rectangle that encompasses the whole of the window, and the
|
||||
* 'content' rectangle that is the area where the controls are drawn.
|
||||
* * The 'content' rectangle is always fully included in the 'frame'
|
||||
* rectangle.
|
||||
* * The 'frame' rectangle is the one that is used to move the window
|
||||
* around.
|
||||
* * All controls coordinates are related to the 'content' rectangle.
|
||||
*
|
||||
* The window 'layer' is used to determine the order of the windows on the
|
||||
* screen, the higher the layer, the more in front the window is.
|
||||
* Windows can be 'selected' to bring them to the front -- that brings
|
||||
* them to the front of their layer, not necessarily the topmost window.
|
||||
*
|
||||
* Windows contain an 'action' list that are called when the window
|
||||
* wants to signal the application; for example when the window is closed,
|
||||
* but it can be used for other things as application requires.
|
||||
*
|
||||
* Mouse clicks are handled by the window, and the window by first
|
||||
* checking if the click is in a control, and if so, passing the event
|
||||
* to the control.
|
||||
* Any control that receives the 'mouse' down will ALSO receive the
|
||||
* mouse DRAG and UP events, even if the mouse has moved outside the
|
||||
* control. This is the meaning of the 'control_clicked' field.
|
||||
*
|
||||
* The 'control_focus' field is used to keep track of the control that
|
||||
* has the keyboard focus. This is used to send key events to the
|
||||
* control that has the focus. That control can still 'refuse' the event,
|
||||
* in which case is it passed in turn to the others, and to the window.
|
||||
*/
|
||||
typedef struct mui_window_t {
|
||||
TAILQ_ENTRY(mui_window_t) self;
|
||||
struct mui_t * ui;
|
||||
mui_wdef_p wdef;
|
||||
uint32_t uid; // optional, pseudo unique id
|
||||
struct {
|
||||
uint hidden: 1,
|
||||
disposed : 1,
|
||||
layer : 4,
|
||||
closebox : 1,
|
||||
style: 4, // specific to the WDEF
|
||||
hit_part : 8, in_part : 8;
|
||||
} flags;
|
||||
c2_pt_t click_loc;
|
||||
// both these rectangles are in screen coordinates, even tho
|
||||
// 'contents' is fully included in 'frame'
|
||||
c2_rect_t frame;
|
||||
c2_rect_t content;
|
||||
char * title;
|
||||
mui_action_queue_t actions;
|
||||
mui_control_group_t main_group; // do not use directly
|
||||
mui_controls_t controls;
|
||||
mui_refqueue_t refs;
|
||||
mui_window_ref_t lock;
|
||||
mui_control_ref_t control_clicked;
|
||||
mui_control_ref_t control_focus;
|
||||
mui_region_t inval;
|
||||
} mui_window_t;
|
||||
|
||||
/*
|
||||
* Window related
|
||||
*/
|
||||
/*
|
||||
* This is the main function to create a window. The
|
||||
* * 'wdef' is the window definition (or NULL for a default window).
|
||||
* see mui_wdef_p for the callback definition.
|
||||
* * 'layer' layer to put it in (default to zero for normal windows)
|
||||
* * 'instance_size' zero (for default) or the size of the window instance
|
||||
* object that is returned, you can therefore have your own custom field
|
||||
* attached to a window.
|
||||
*/
|
||||
mui_window_t *
|
||||
mui_window_create(
|
||||
struct mui_t * ui,
|
||||
c2_rect_t frame,
|
||||
mui_wdef_p wdef,
|
||||
uint32_t layer_flags,
|
||||
const char * title,
|
||||
uint32_t instance_size);
|
||||
// Dispose of a window and it's content (controls).
|
||||
/*
|
||||
* Note: if the window is 'locked' the window is not freed immediately.
|
||||
* This is to prevent re-entrance problems. This allows window actions to
|
||||
* delete their own window without crashing.
|
||||
*/
|
||||
void
|
||||
mui_window_dispose(
|
||||
mui_window_t * win);
|
||||
// Invalidate 'r' in window coordinates, or the whole window if 'r' is NULL
|
||||
void
|
||||
mui_window_inval(
|
||||
mui_window_t * win,
|
||||
c2_rect_t * r);
|
||||
// return true if the window is the frontmost window (in that window's layer)
|
||||
bool
|
||||
mui_window_isfront(
|
||||
mui_window_t * win);
|
||||
// return the top (non menubar/menu) window
|
||||
mui_window_t *
|
||||
mui_window_front(
|
||||
struct mui_t *ui);
|
||||
|
||||
// move win to the front (of its layer), return true if it was moved
|
||||
bool
|
||||
mui_window_select(
|
||||
mui_window_t * win);
|
||||
// call the window action callback(s), if any
|
||||
void
|
||||
mui_window_action(
|
||||
mui_window_t * c,
|
||||
uint32_t what,
|
||||
void * param );
|
||||
// Add an action callback for this window
|
||||
void
|
||||
mui_window_set_action(
|
||||
mui_window_t * c,
|
||||
mui_window_action_p cb,
|
||||
void * param );
|
||||
// return the window whose UID is 'uid', or NULL if not found
|
||||
mui_window_t *
|
||||
mui_window_get_by_id(
|
||||
struct mui_t * ui,
|
||||
uint32_t uid );
|
||||
// set the window UID
|
||||
void
|
||||
mui_window_set_id(
|
||||
mui_window_t * win,
|
||||
uint32_t uid);
|
@ -79,13 +79,13 @@ mui_alert(
|
||||
MUI_TEXT_ALIGN_CENTER | MUI_TEXT_ALIGN_MIDDLE |
|
||||
MUI_TEXT_ALIGN_COMPACT);
|
||||
|
||||
c = NULL;
|
||||
TAILQ_FOREACH(c, &w->controls, self) {
|
||||
if (mui_control_get_uid(c) == 0)
|
||||
continue;
|
||||
mui_control_set_action(c, _mui_alert_button_cb, alert);
|
||||
c = mui_controls_first(&w->controls, MUI_CONTROLS_ALL);
|
||||
while (c) {
|
||||
if (mui_control_get_uid(c) != 0) {
|
||||
mui_control_set_action(c, _mui_alert_button_cb, alert);
|
||||
}
|
||||
c = mui_controls_next(c, MUI_CONTROLS_ALL);
|
||||
}
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
|
@ -14,12 +14,25 @@
|
||||
|
||||
enum {
|
||||
MUI_CONTROL_BUTTON = FCC('b','u','t','n'),
|
||||
|
||||
};
|
||||
|
||||
enum mui_but_part_e {
|
||||
MUI_BUT_PART_FRAME = 0,
|
||||
MUI_BUT_PART_TEXT,
|
||||
MUI_BUT_PART_ICON, // optional, if 'icon' has a glyph
|
||||
MUI_BUT_PART_COUNT,
|
||||
};
|
||||
|
||||
typedef struct mui_button_control_t {
|
||||
mui_control_t control;
|
||||
mui_text_e icon_align;
|
||||
char icon[8]; // UTF8 icon
|
||||
} mui_button_control_t;
|
||||
|
||||
extern const mui_control_color_t mui_control_color[MUI_CONTROL_STATE_COUNT];
|
||||
|
||||
#define BUTTON_INSET 4
|
||||
|
||||
void
|
||||
mui_button_draw(
|
||||
mui_window_t * win,
|
||||
@ -45,6 +58,14 @@ mui_button_draw(
|
||||
mui_font_text_measure(main, c->title, &m);
|
||||
|
||||
int title_width = m.x1;// - m.x0;
|
||||
mui_button_control_t * but = (mui_button_control_t *)c;
|
||||
mui_font_t * icons = NULL;
|
||||
if (but->icon[0]) {
|
||||
icons = mui_font_find(win->ui, "icon_small");
|
||||
stb_ttc_measure m2 = {};
|
||||
mui_font_text_measure(icons, but->icon, &m2);
|
||||
title_width += m2.x1 - m2.x0 + 8;
|
||||
}
|
||||
c2_rect_t title = C2_RECT_WH(0, 0, title_width, m.ascent - m.descent);
|
||||
c2_rect_offset(&title,
|
||||
f.l + ((c2_rect_width(&f) / 2) - (c2_rect_width(&title)) / 2),
|
||||
@ -56,6 +77,8 @@ mui_button_draw(
|
||||
cg_set_line_width(cg, 2);
|
||||
cg_round_rectangle(cg, inner.l, inner.t,
|
||||
c2_rect_width(&inner), c2_rect_height(&inner), 6, 6);
|
||||
// cg_rectangle(cg, title.l, title.t,
|
||||
// c2_rect_width(&title), c2_rect_height(&title));
|
||||
cg_set_source_color(cg, &CG_COLOR(mui_control_color[c->state].fill));
|
||||
cg_fill_preserve(cg);
|
||||
// cg_rectangle(cg, title.l, title.t,
|
||||
@ -63,6 +86,16 @@ mui_button_draw(
|
||||
cg_set_source_color(cg, &CG_COLOR(mui_control_color[c->state].frame));
|
||||
cg_stroke(cg);
|
||||
// offset for leading space
|
||||
if (but->icon[0]) {
|
||||
stb_ttc_measure m2 = {};
|
||||
mui_font_text_measure(icons, but->icon, &m2);
|
||||
c2_rect_t icon = C2_RECT_WH(0, 0, m2.x1 - m2.x0, m2.ascent - m2.descent);
|
||||
c2_rect_offset(&icon, title.l, title.t - 1);
|
||||
// c2_rect_offset(&icon, -c2_rect_width(&icon) - 8, 0);
|
||||
mui_font_text_draw(icons, dr, icon.tl, but->icon, 0,
|
||||
mui_control_color[c->state].text);
|
||||
title.l = icon.r + 8;
|
||||
}
|
||||
mui_font_text_draw(main, dr,
|
||||
C2_PT(title.l - m.x0, title.t), c->title, strlen(c->title),
|
||||
mui_control_color[c->state].text);
|
||||
@ -168,8 +201,10 @@ mui_button_mouse(
|
||||
* their values off, before setting this one to on */
|
||||
case MUI_BUTTON_STYLE_RADIO: {
|
||||
if (c->uid_mask) {
|
||||
mui_control_t * c2 = NULL;
|
||||
TAILQ_FOREACH(c2, &c->win->controls, self) {
|
||||
mui_control_t * c2 = mui_controls_first(
|
||||
&c->win->controls,
|
||||
MUI_CONTROLS_ALL);
|
||||
while (c2) {
|
||||
if (c2->type == MUI_CONTROL_BUTTON &&
|
||||
c2->style == MUI_BUTTON_STYLE_RADIO &&
|
||||
(c2->uid & c->uid_mask) == (c->uid & c->uid_mask) &&
|
||||
@ -177,6 +212,7 @@ mui_button_mouse(
|
||||
// printf("OFF %4.4s\n", (char*)&c2->uid);
|
||||
mui_control_set_value(c2, false);
|
||||
}
|
||||
c2 = mui_controls_next(c2, MUI_CONTROLS_ALL);
|
||||
}
|
||||
}
|
||||
// printf("ON %4.4s\n", (char*)&c->uid);
|
||||
@ -272,7 +308,18 @@ mui_button_new(
|
||||
}
|
||||
mui_control_t * c = mui_control_new(
|
||||
win, MUI_CONTROL_BUTTON, mui_cdef_button,
|
||||
frame, title, uid, 0);
|
||||
frame, title, uid, sizeof(mui_button_control_t));
|
||||
c->style = style;
|
||||
return c;
|
||||
}
|
||||
|
||||
void
|
||||
mui_button_set_icon(
|
||||
mui_control_t * c,
|
||||
const char * icon,
|
||||
mui_text_e align )
|
||||
{
|
||||
mui_button_control_t * but = (mui_button_control_t *)c;
|
||||
snprintf(but->icon, sizeof(but->icon), "%s", icon);
|
||||
but->icon_align = align;
|
||||
}
|
@ -166,6 +166,7 @@ mui_listbox_key(
|
||||
c2_rect_offset(&f, -f.l, -f.t);
|
||||
uint32_t page_size = (c2_rect_height(&f) / lb->elem_height)-1;
|
||||
|
||||
bool res = false;
|
||||
int delta = 0;
|
||||
if (ev->modifiers & (MUI_MODIFIER_SUPER | MUI_MODIFIER_CTRL))
|
||||
return false;
|
||||
@ -176,10 +177,11 @@ mui_listbox_key(
|
||||
case MUI_KEY_DOWN: delta = 1; break;
|
||||
case MUI_KEY_PAGEUP: delta = -page_size; break;
|
||||
case MUI_KEY_PAGEDOWN: delta = page_size; break;
|
||||
case '\t':
|
||||
case MUI_KEY_TAB:
|
||||
mui_control_switch_focus(c->win,
|
||||
ev->modifiers & MUI_MODIFIER_SHIFT ? -1 : 0);
|
||||
break;
|
||||
return true;
|
||||
// break;
|
||||
#if 0
|
||||
case 13: // enter
|
||||
mui_control_action(c, MUI_CONTROL_ACTION_SELECT,
|
||||
@ -189,8 +191,12 @@ mui_listbox_key(
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!delta && isalpha(ev->key.key)) {
|
||||
delta = mui_listbox_typehead(lb, ev);
|
||||
res = true;
|
||||
}
|
||||
if (!delta)
|
||||
return false;
|
||||
return res;
|
||||
int nsel = c->value + delta;
|
||||
if (nsel < 0)
|
||||
nsel = 0;
|
||||
@ -219,7 +225,7 @@ mui_listbox_key(
|
||||
&lb->elems.e[nsel]);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool
|
||||
|
@ -44,6 +44,7 @@
|
||||
|
||||
#include "mui.h"
|
||||
#include "cg.h"
|
||||
#include "mui_cdef_te_priv.h"
|
||||
|
||||
enum {
|
||||
MUI_CONTROL_TEXTEDIT = FCC('T','e','a','c'),
|
||||
@ -57,72 +58,9 @@ enum {
|
||||
// MUI_TE_SELECTING_LINES, // TODO?
|
||||
};
|
||||
|
||||
/*
|
||||
* This describes a text edit action, either we insert some text at some position,
|
||||
* or we delete some text at some position.
|
||||
* These actions are queued in a TAILQ, so we can undo/redo them.
|
||||
* The text is UTF8, and the position is a BYTE index in the text (not a glyph).
|
||||
*
|
||||
* We preallocate a fixed number of actions, and when we reach the limit, we
|
||||
* start reusing the oldest ones. This limits the number of undo/redo actions
|
||||
* to something sensible.
|
||||
*/
|
||||
typedef struct mui_te_action_t {
|
||||
TAILQ_ENTRY(mui_te_action_t) self;
|
||||
uint insert : 1; // if not insert, its a delete
|
||||
uint32_t position, length;
|
||||
mui_utf8_t text;
|
||||
} mui_te_action_t;
|
||||
|
||||
// action queue
|
||||
typedef TAILQ_HEAD(mui_te_action_queue_t, mui_te_action_t) mui_te_action_queue_t;
|
||||
|
||||
/*
|
||||
* This describes the selection in the text-edit, it can either be a carret,
|
||||
* or a selection of text. The selection is kept as a start and end glyph index,
|
||||
* and the drawing code calculates the rectangles for the selection.
|
||||
*/
|
||||
typedef struct mui_sel_t {
|
||||
uint carret: 1; // carret is visible (if sel.start == end)
|
||||
uint start, end; // glyph index in text
|
||||
// rectangles for the first partial line, the body,
|
||||
// and the last partial line. All of them can be empty
|
||||
union {
|
||||
struct {
|
||||
c2_rect_t first, body, last;
|
||||
};
|
||||
c2_rect_t e[3];
|
||||
};
|
||||
} mui_sel_t;
|
||||
|
||||
typedef struct mui_textedit_control_t {
|
||||
mui_control_t control;
|
||||
uint trace : 1; // debug trace
|
||||
uint32_t flags; // display flags
|
||||
mui_sel_t sel;
|
||||
mui_font_t * font;
|
||||
mui_utf8_t text;
|
||||
mui_glyph_line_array_t measure;
|
||||
c2_pt_t margin;
|
||||
c2_rect_t text_content;
|
||||
struct {
|
||||
uint start, end;
|
||||
} click;
|
||||
uint selecting_mode;
|
||||
} mui_textedit_control_t;
|
||||
|
||||
extern const mui_control_color_t mui_control_color[MUI_CONTROL_STATE_COUNT];
|
||||
|
||||
|
||||
static void
|
||||
_mui_textedit_select_signed(
|
||||
mui_textedit_control_t * te,
|
||||
int glyph_start,
|
||||
int glyph_end);
|
||||
static void
|
||||
_mui_textedit_refresh_sel(
|
||||
mui_textedit_control_t * te,
|
||||
mui_sel_t * sel);
|
||||
static bool
|
||||
mui_cdef_textedit(
|
||||
struct mui_control_t * c,
|
||||
@ -134,7 +72,7 @@ mui_cdef_textedit(
|
||||
* which means they are already offset by margin.x, margin.y
|
||||
* and the text_content.tl.x, text_content.tl.y
|
||||
*/
|
||||
static void
|
||||
void
|
||||
_mui_textedit_inval(
|
||||
mui_textedit_control_t * te,
|
||||
c2_rect_t r)
|
||||
@ -166,7 +104,7 @@ _mui_textedit_carret_timer(
|
||||
}
|
||||
|
||||
/* this 'forces' the carret to be visible, used when typing */
|
||||
static void
|
||||
void
|
||||
_mui_textedit_show_carret(
|
||||
mui_textedit_control_t * te)
|
||||
{
|
||||
@ -183,7 +121,7 @@ _mui_textedit_show_carret(
|
||||
}
|
||||
|
||||
/* Return the line number, and glyph position in line a glyph index */
|
||||
static int
|
||||
int
|
||||
_mui_glyph_to_line_index(
|
||||
mui_glyph_line_array_t * measure,
|
||||
uint glyph_pos,
|
||||
@ -259,7 +197,7 @@ _mui_point_to_line_index(
|
||||
}
|
||||
|
||||
/* Return the glyph position in the text for line number and index in line */
|
||||
static uint
|
||||
uint
|
||||
_mui_line_index_to_glyph(
|
||||
mui_glyph_line_array_t * measure,
|
||||
uint line,
|
||||
@ -295,7 +233,7 @@ _mui_line_index_to_glyph_word(
|
||||
}
|
||||
|
||||
/* Convert a glyph index to a byte index (used to manipulate text array) */
|
||||
static uint
|
||||
uint
|
||||
_mui_glyph_to_byte_offset(
|
||||
mui_glyph_line_array_t * measure,
|
||||
uint glyph_pos)
|
||||
@ -316,222 +254,7 @@ _mui_glyph_to_byte_offset(
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the 3 rectangles that represent the graphical selection.
|
||||
* The 'start' is the first line of the selection, or the position of the
|
||||
* carret if the selection is empty.
|
||||
* The other two are 'optional' (they can be empty), and represent the last
|
||||
* line of the selection, and the body of the selection that is the rectangle
|
||||
* between the first and last line.
|
||||
*/
|
||||
static int
|
||||
_mui_make_sel_rects(
|
||||
mui_glyph_line_array_t * measure,
|
||||
mui_font_t * font,
|
||||
mui_sel_t * sel,
|
||||
c2_rect_t frame)
|
||||
{
|
||||
if (!measure->count)
|
||||
return -1;
|
||||
sel->last = sel->first = sel->body = (c2_rect_t) {};
|
||||
uint start_line, start_index;
|
||||
uint end_line, end_index;
|
||||
_mui_glyph_to_line_index(measure, sel->start, &start_line, &start_index);
|
||||
_mui_glyph_to_line_index(measure, sel->end, &end_line, &end_index);
|
||||
mui_glyph_array_t * line = &measure->e[start_line];
|
||||
|
||||
if (start_line == end_line) {
|
||||
// single line selection
|
||||
sel->first = (c2_rect_t) {
|
||||
.l = frame.l + line->e[start_index].x,
|
||||
.t = frame.t + line->t,
|
||||
.r = frame.l + line->e[end_index].x,
|
||||
.b = frame.t + line->b,
|
||||
};
|
||||
return 0;
|
||||
}
|
||||
// first line
|
||||
sel->first = (c2_rect_t) {
|
||||
.l = frame.l + line->e[start_index].x, .t = frame.t + line->t,
|
||||
.r = frame.r, .b = frame.t + line->b,
|
||||
};
|
||||
// last line
|
||||
line = &measure->e[end_line];
|
||||
sel->last = (c2_rect_t) {
|
||||
.l = frame.l, .t = frame.t + line->t,
|
||||
.r = frame.l + line->e[end_index].x, .b = frame.t + line->b,
|
||||
};
|
||||
// body
|
||||
sel->body = (c2_rect_t) {
|
||||
.l = frame.l, .t = sel->first.b,
|
||||
.r = frame.r, .b = sel->last.t,
|
||||
};
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Refresh the whole selection (or around the carret selection) */
|
||||
static void
|
||||
_mui_textedit_refresh_sel(
|
||||
mui_textedit_control_t * te,
|
||||
mui_sel_t * sel)
|
||||
{
|
||||
if (!sel)
|
||||
sel = &te->sel;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
c2_rect_t r = te->sel.e[i];
|
||||
if (i == 0 && te->sel.start == te->sel.end) {
|
||||
c2_rect_inset(&r, -1, -1);
|
||||
// printf("refresh_sel: carret %s\n", c2_rect_as_str(&r));
|
||||
}
|
||||
if (!c2_rect_isempty(&r))
|
||||
_mui_textedit_inval(te, r);
|
||||
}
|
||||
}
|
||||
|
||||
/* this makes sure the text is always visible in the frame */
|
||||
static void
|
||||
_mui_textedit_clamp_text_frame(
|
||||
mui_textedit_control_t * te)
|
||||
{
|
||||
c2_rect_t f = te->control.frame;
|
||||
c2_rect_offset(&f, -f.l, -f.t);
|
||||
if (te->flags & MUI_CONTROL_TEXTBOX_FRAME)
|
||||
c2_rect_inset(&f, te->margin.x, te->margin.y);
|
||||
c2_rect_t old = te->text_content;
|
||||
te->text_content.r = te->text_content.l + te->measure.margin_right;
|
||||
te->text_content.b = te->text_content.t + te->measure.height;
|
||||
D(printf(" %s %s / %3dx%3d\n", __func__,
|
||||
c2_rect_as_str(&te->text_content),
|
||||
c2_rect_width(&f), c2_rect_height(&f));)
|
||||
if (te->text_content.b < c2_rect_height(&f))
|
||||
c2_rect_offset(&te->text_content, 0,
|
||||
c2_rect_height(&f) - te->text_content.b);
|
||||
if (te->text_content.t > f.t)
|
||||
c2_rect_offset(&te->text_content, 0, f.t - te->text_content.t);
|
||||
if (te->text_content.r < c2_rect_width(&f))
|
||||
c2_rect_offset(&te->text_content,
|
||||
c2_rect_width(&f) - te->text_content.r, 0);
|
||||
if (te->text_content.l > f.l)
|
||||
c2_rect_offset(&te->text_content, f.l - te->text_content.l, 0);
|
||||
if (c2_rect_equal(&te->text_content, &old))
|
||||
return;
|
||||
D(printf(" clamped TE from %s to %s\n", c2_rect_as_str(&old),
|
||||
c2_rect_as_str(&te->text_content));)
|
||||
mui_control_inval(&te->control);
|
||||
}
|
||||
|
||||
/* This scrolls the view following the carret, used when typing.
|
||||
* This doesn't check for out of bounds, but the clamping should
|
||||
* have made sure the text is always visible. */
|
||||
static void
|
||||
_mui_textedit_ensure_carret_visible(
|
||||
mui_textedit_control_t * te)
|
||||
{
|
||||
c2_rect_t f = te->control.frame;
|
||||
// c2_rect_offset(&f, -f.l, -f.t);
|
||||
if (te->flags & MUI_CONTROL_TEXTBOX_FRAME)
|
||||
c2_rect_inset(&f, te->margin.x, te->margin.y);
|
||||
if (te->sel.start != te->sel.end)
|
||||
return;
|
||||
c2_rect_t old = te->text_content;
|
||||
c2_rect_t r = te->sel.first;
|
||||
D(printf("%s carret %s frame %s\n", __func__,
|
||||
c2_rect_as_str(&r), c2_rect_as_str(&f));)
|
||||
c2_rect_offset(&r, -te->text_content.l, -te->text_content.t);
|
||||
if (r.r < f.l) {
|
||||
D(printf(" moved TE LEFT %d\n", -(f.l - r.r));)
|
||||
c2_rect_offset(&te->text_content, -(f.l - r.l), 0);
|
||||
}
|
||||
if (r.l > f.r) {
|
||||
D(printf(" moved TE RIGHT %d\n", -(r.l - f.r));)
|
||||
c2_rect_offset(&te->text_content, -(r.l - f.r), 0);
|
||||
}
|
||||
if (r.t < f.t)
|
||||
c2_rect_offset(&te->text_content, 0, r.t - f.t);
|
||||
if (r.b > f.b)
|
||||
c2_rect_offset(&te->text_content, 0, r.b - f.b);
|
||||
if (c2_rect_equal(&te->text_content, &old))
|
||||
return;
|
||||
D(printf(" moved TE from %s to %s\n", c2_rect_as_str(&old),
|
||||
c2_rect_as_str(&te->text_content));)
|
||||
_mui_textedit_clamp_text_frame(te);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is to be called when the text changes, or the frame (width) changes
|
||||
*/
|
||||
static void
|
||||
_mui_textedit_refresh_measure(
|
||||
mui_textedit_control_t * te)
|
||||
{
|
||||
c2_rect_t f = te->control.frame;
|
||||
c2_rect_offset(&f, -f.l, -f.t);
|
||||
if (te->flags & MUI_CONTROL_TEXTBOX_FRAME)
|
||||
c2_rect_inset(&f, te->margin.x, te->margin.y);
|
||||
if (!(te->flags & MUI_CONTROL_TEXTEDIT_VERTICAL))
|
||||
f.r = 0x7fff; // make it very large, we don't want wrapping.
|
||||
|
||||
mui_glyph_line_array_t new_measure = {};
|
||||
|
||||
mui_font_measure(te->font, f,
|
||||
(const char*)te->text.e, te->text.count-1,
|
||||
&new_measure, te->flags);
|
||||
|
||||
f = te->control.frame;
|
||||
if (te->flags & MUI_CONTROL_TEXTBOX_FRAME)
|
||||
c2_rect_inset(&f, te->margin.x, te->margin.y);
|
||||
// Refresh the lines that have changed. Perhaps all of them did,
|
||||
// doesn't matter, but it's nice to avoid redrawing the whole text
|
||||
// when someone is typing.
|
||||
for (uint i = 0; i < new_measure.count && i < te->measure.count; i++) {
|
||||
if (i >= te->measure.count) {
|
||||
c2_rect_t r = f;
|
||||
r.t += new_measure.e[i].t;
|
||||
r.b = r.t + new_measure.e[i].b;
|
||||
r.r = new_measure.e[i].x + new_measure.e[i].w;
|
||||
_mui_textedit_inval(te, r);
|
||||
} else if (i >= new_measure.count) {
|
||||
c2_rect_t r = f;
|
||||
r.t += te->measure.e[i].t;
|
||||
r.b = r.t + te->measure.e[i].b;
|
||||
r.r = te->measure.e[i].x + te->measure.e[i].w;
|
||||
_mui_textedit_inval(te, r);
|
||||
} else {
|
||||
int dirty = 0;
|
||||
// unsure if this could happen, but let's be safe --
|
||||
// technically we should refresh BOTH rectangles (old, new)
|
||||
if (new_measure.e[i].t != te->measure.e[i].t ||
|
||||
new_measure.e[i].b != te->measure.e[i].b) {
|
||||
dirty = 1;
|
||||
} else if (new_measure.e[i].x != te->measure.e[i].x ||
|
||||
new_measure.e[i].count != te->measure.e[i].count ||
|
||||
new_measure.e[i].w != te->measure.e[i].w)
|
||||
dirty = 1;
|
||||
else {
|
||||
for (uint x = 0; x < new_measure.e[i].count; x++) {
|
||||
if (new_measure.e[i].e[x].glyph != te->measure.e[i].e[x].glyph ||
|
||||
new_measure.e[i].e[x].x != te->measure.e[i].e[x].x ||
|
||||
new_measure.e[i].e[x].w != te->measure.e[i].e[x].w) {
|
||||
dirty = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dirty) {
|
||||
c2_rect_t r = f;
|
||||
r.t += new_measure.e[i].t;
|
||||
r.b = r.t + new_measure.e[i].b;
|
||||
r.r = new_measure.e[i].x + new_measure.e[i].w;
|
||||
_mui_textedit_inval(te, r);
|
||||
}
|
||||
}
|
||||
}
|
||||
mui_font_measure_clear(&te->measure);
|
||||
te->measure = new_measure;
|
||||
_mui_textedit_clamp_text_frame(te);
|
||||
}
|
||||
|
||||
static void
|
||||
void
|
||||
_mui_textedit_sel_delete(
|
||||
mui_textedit_control_t * te,
|
||||
bool re_measure,
|
||||
@ -550,27 +273,8 @@ _mui_textedit_sel_delete(
|
||||
te->sel.start, te->sel.start);
|
||||
}
|
||||
|
||||
void
|
||||
mui_textedit_set_text(
|
||||
mui_control_t * c,
|
||||
const char * text)
|
||||
{
|
||||
mui_textedit_control_t *te = (mui_textedit_control_t *)c;
|
||||
mui_utf8_clear(&te->text);
|
||||
int tl = strlen(text);
|
||||
mui_utf8_realloc(&te->text, tl + 1);
|
||||
memcpy(te->text.e, text, tl + 1);
|
||||
/*
|
||||
* Note, the text.count *counts the terminating zero*
|
||||
*/
|
||||
te->text.count = tl + 1;
|
||||
if (!te->font)
|
||||
te->font = mui_font_find(c->win->ui, "main");
|
||||
_mui_textedit_refresh_measure(te);
|
||||
}
|
||||
|
||||
/* this one allows passing -1 etc, which is handy of cursor movement */
|
||||
static void
|
||||
void
|
||||
_mui_textedit_select_signed(
|
||||
mui_textedit_control_t * te,
|
||||
int glyph_start,
|
||||
@ -581,7 +285,7 @@ _mui_textedit_select_signed(
|
||||
if (glyph_end < 0)
|
||||
glyph_end = 0;
|
||||
if (glyph_end > (int)te->text.count)
|
||||
glyph_end = te->text.count;
|
||||
glyph_end = te->text.count+1;
|
||||
if (glyph_start > (int)te->text.count)
|
||||
glyph_start = te->text.count;
|
||||
if (glyph_start > glyph_end) {
|
||||
@ -590,7 +294,7 @@ _mui_textedit_select_signed(
|
||||
glyph_end = t;
|
||||
}
|
||||
|
||||
// printf("%s %d:%d\n", __func__, glyph_start, glyph_end);
|
||||
printf("%s %d:%d\n", __func__, glyph_start, glyph_end);
|
||||
c2_rect_t f = te->control.frame;
|
||||
if (te->flags & MUI_CONTROL_TEXTBOX_FRAME)
|
||||
c2_rect_inset(&f, te->margin.x, te->margin.y);
|
||||
@ -605,19 +309,12 @@ _mui_textedit_select_signed(
|
||||
}
|
||||
|
||||
/*
|
||||
* Mark old selection as invalid, and set the new one,
|
||||
* and make sure it's visible
|
||||
*/
|
||||
void
|
||||
mui_textedit_set_selection(
|
||||
mui_control_t * c,
|
||||
uint glyph_start,
|
||||
uint glyph_end)
|
||||
{
|
||||
mui_textedit_control_t *te = (mui_textedit_control_t *)c;
|
||||
_mui_textedit_select_signed(te, glyph_start, glyph_end);
|
||||
}
|
||||
|
||||
██████ ██████ █████ ██ ██ ██ ███ ██ ██████
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██
|
||||
██ ██ ██████ ███████ ██ █ ██ ██ ██ ██ ██ ██ ███
|
||||
██ ██ ██ ██ ██ ██ ██ ███ ██ ██ ██ ██ ██ ██ ██
|
||||
██████ ██ ██ ██ ██ ███ ███ ██ ██ ████ ██████
|
||||
*/
|
||||
static void
|
||||
mui_textedit_draw(
|
||||
mui_window_t * win,
|
||||
@ -743,8 +440,15 @@ done:
|
||||
mui_drawable_clip_pop(dr);
|
||||
}
|
||||
|
||||
/*
|
||||
███ ███ ██████ ██ ██ ███████ ███████
|
||||
████ ████ ██ ██ ██ ██ ██ ██
|
||||
██ ████ ██ ██ ██ ██ ██ ███████ █████
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ██████ ██████ ███████ ███████
|
||||
*/
|
||||
static bool
|
||||
mui_textedit_mouse(
|
||||
_mui_textedit_mouse(
|
||||
struct mui_control_t * c,
|
||||
mui_event_t * ev)
|
||||
{
|
||||
@ -871,172 +575,6 @@ mui_textedit_mouse(
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool
|
||||
mui_textedit_key(
|
||||
struct mui_control_t * c,
|
||||
mui_event_t * ev)
|
||||
{
|
||||
mui_textedit_control_t *te = (mui_textedit_control_t *)c;
|
||||
|
||||
_mui_textedit_show_carret(te);
|
||||
mui_glyph_line_array_t * me = &te->measure;
|
||||
if (ev->modifiers & MUI_MODIFIER_CTRL) {
|
||||
switch (ev->key.key) {
|
||||
case 'T': {
|
||||
te->trace = !te->trace;
|
||||
printf("TRACE %s\n", te->trace ? "ON" : "OFF");
|
||||
} break;
|
||||
case 'D': {// dump text status and measures lines
|
||||
printf("Text:\n'%s'\n", te->text.e);
|
||||
printf("Text count: %d\n", te->text.count);
|
||||
printf("Text measure: %d\n", me->count);
|
||||
for (uint i = 0; i < me->count; i++) {
|
||||
mui_glyph_array_t * line = &me->e[i];
|
||||
printf(" line %d: %d\n", i, line->count);
|
||||
for (uint j = 0; j < line->count; j++) {
|
||||
mui_glyph_t * g = &line->e[j];
|
||||
printf(" %3d: %04x:%c x:%3f w:%3d\n",
|
||||
j, te->text.e[g->pos],
|
||||
te->text.e[g->pos] < ' ' ?
|
||||
'.' : te->text.e[g->pos],
|
||||
g->x, g->w);
|
||||
}
|
||||
}
|
||||
te->flags |= MUI_TEXT_DEBUG;
|
||||
} break;
|
||||
case 'a': {
|
||||
_mui_textedit_select_signed(te, 0, te->text.count-1);
|
||||
} break;
|
||||
case 'c': {
|
||||
if (te->sel.start != te->sel.end) {
|
||||
uint32_t start = _mui_glyph_to_byte_offset(me, te->sel.start);
|
||||
uint32_t end = _mui_glyph_to_byte_offset(me, te->sel.end);
|
||||
mui_clipboard_set(c->win->ui,
|
||||
te->text.e + start, end - start);
|
||||
}
|
||||
} break;
|
||||
case 'x': {
|
||||
if (te->sel.start != te->sel.end) {
|
||||
uint32_t start = _mui_glyph_to_byte_offset(me, te->sel.start);
|
||||
uint32_t end = _mui_glyph_to_byte_offset(me, te->sel.end);
|
||||
mui_clipboard_set(c->win->ui,
|
||||
te->text.e + start, end - start);
|
||||
_mui_textedit_sel_delete(te, true, true);
|
||||
}
|
||||
} break;
|
||||
case 'v': {
|
||||
uint32_t len;
|
||||
const uint8_t * clip = mui_clipboard_get(c->win->ui, &len);
|
||||
if (clip) {
|
||||
if (te->sel.start != te->sel.end)
|
||||
_mui_textedit_sel_delete(te, true, true);
|
||||
mui_utf8_insert(&te->text,
|
||||
_mui_glyph_to_byte_offset(me, te->sel.start),
|
||||
clip, len);
|
||||
_mui_textedit_refresh_measure(te);
|
||||
_mui_textedit_select_signed(te,
|
||||
te->sel.start + len, te->sel.start + len);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
switch (ev->key.key) {
|
||||
case MUI_KEY_UP: {
|
||||
uint line, index;
|
||||
_mui_glyph_to_line_index(me, te->sel.start, &line, &index);
|
||||
if (line > 0) {
|
||||
uint pos = _mui_line_index_to_glyph(me, line-1, index);
|
||||
if (ev->modifiers & MUI_MODIFIER_SHIFT) {
|
||||
_mui_textedit_select_signed(te, te->sel.start, pos);
|
||||
} else {
|
||||
_mui_textedit_select_signed(te, pos, pos);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case MUI_KEY_DOWN: {
|
||||
uint line, index;
|
||||
_mui_glyph_to_line_index(me, te->sel.start, &line, &index);
|
||||
if (line < me->count-1) {
|
||||
uint pos = _mui_line_index_to_glyph(me, line+1, index);
|
||||
if (ev->modifiers & MUI_MODIFIER_SHIFT) {
|
||||
_mui_textedit_select_signed(te, te->sel.start, pos);
|
||||
} else {
|
||||
_mui_textedit_select_signed(te, pos, pos);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case MUI_KEY_LEFT: {
|
||||
if (ev->modifiers & MUI_MODIFIER_SHIFT) {
|
||||
_mui_textedit_select_signed(te, te->sel.start - 1, te->sel.end);
|
||||
} else {
|
||||
if (te->sel.start == te->sel.end)
|
||||
_mui_textedit_select_signed(te, te->sel.start - 1, te->sel.start - 1);
|
||||
else
|
||||
_mui_textedit_select_signed(te, te->sel.start, te->sel.start);
|
||||
}
|
||||
} break;
|
||||
case MUI_KEY_RIGHT: {
|
||||
if (ev->modifiers & MUI_MODIFIER_SHIFT) {
|
||||
_mui_textedit_select_signed(te, te->sel.start, te->sel.end + 1);
|
||||
} else {
|
||||
if (te->sel.start == te->sel.end)
|
||||
_mui_textedit_select_signed(te, te->sel.start + 1, te->sel.start + 1);
|
||||
else
|
||||
_mui_textedit_select_signed(te, te->sel.end, te->sel.end);
|
||||
}
|
||||
} break;
|
||||
case MUI_KEY_BACKSPACE: {
|
||||
if (te->sel.start == te->sel.end) {
|
||||
if (te->sel.start > 0) {
|
||||
mui_utf8_delete(&te->text,
|
||||
_mui_glyph_to_byte_offset(me, te->sel.start - 1),
|
||||
1);
|
||||
_mui_textedit_refresh_measure(te);
|
||||
_mui_textedit_select_signed(te, te->sel.start - 1, te->sel.start - 1);
|
||||
}
|
||||
} else {
|
||||
_mui_textedit_sel_delete(te, true, true);
|
||||
}
|
||||
} break;
|
||||
case MUI_KEY_DELETE: {
|
||||
if (te->sel.start == te->sel.end) {
|
||||
if (te->sel.start < te->text.count-1) {
|
||||
mui_utf8_delete(&te->text,
|
||||
_mui_glyph_to_byte_offset(me, te->sel.start), 1);
|
||||
_mui_textedit_refresh_measure(te);
|
||||
_mui_textedit_select_signed(te, te->sel.start, te->sel.start);
|
||||
}
|
||||
} else {
|
||||
_mui_textedit_sel_delete(te, true, true);
|
||||
}
|
||||
} break;
|
||||
case '\t': {
|
||||
mui_control_switch_focus(c->win,
|
||||
ev->modifiers & MUI_MODIFIER_SHIFT ? -1 : 0);
|
||||
} break;
|
||||
default:
|
||||
printf("%s key 0x%x\n", __func__, ev->key.key);
|
||||
if (ev->key.key == 13 && !(te->flags & MUI_CONTROL_TEXTEDIT_VERTICAL))
|
||||
return false;
|
||||
if (ev->key.key == 13 ||
|
||||
(ev->key.key >= 32 && ev->key.key < 127)) {
|
||||
if (te->sel.start != te->sel.end) {
|
||||
_mui_textedit_sel_delete(te, false, false);
|
||||
_mui_textedit_select_signed(te, te->sel.start, te->sel.start);
|
||||
}
|
||||
uint8_t k = ev->key.key;
|
||||
mui_utf8_insert(&te->text,
|
||||
_mui_glyph_to_byte_offset(me, te->sel.start), &k, 1);
|
||||
_mui_textedit_refresh_measure(te);
|
||||
_mui_textedit_select_signed(te,
|
||||
te->sel.start + 1, te->sel.start + 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
mui_cdef_textedit(
|
||||
struct mui_control_t * c,
|
||||
@ -1082,10 +620,10 @@ mui_cdef_textedit(
|
||||
case MUI_EVENT_BUTTONUP:
|
||||
case MUI_EVENT_DRAG:
|
||||
case MUI_EVENT_BUTTONDOWN: {
|
||||
return mui_textedit_mouse(c, ev);
|
||||
return _mui_textedit_mouse(c, ev);
|
||||
} break;
|
||||
case MUI_EVENT_KEYDOWN: {
|
||||
return mui_textedit_key(c, ev);
|
||||
return _mui_textedit_key(c, ev);
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
@ -1116,3 +654,67 @@ mui_textedit_control_new(
|
||||
te->margin = (c2_pt_t){ .x = 4, .y = 2 };
|
||||
return &te->control;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Mark old selection as invalid, and set the new one,
|
||||
* and make sure it's visible
|
||||
*/
|
||||
void
|
||||
mui_textedit_set_selection(
|
||||
mui_control_t * c,
|
||||
uint glyph_start,
|
||||
uint glyph_end)
|
||||
{
|
||||
mui_textedit_control_t *te = (mui_textedit_control_t *)c;
|
||||
printf("%s %d:%d\n", __func__, glyph_start, glyph_end);
|
||||
_mui_textedit_select_signed(te, glyph_start, glyph_end);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get current selection
|
||||
*/
|
||||
void
|
||||
mui_textedit_get_selection(
|
||||
mui_control_t * c,
|
||||
uint * glyph_start,
|
||||
uint * glyph_end)
|
||||
{
|
||||
mui_textedit_control_t *te = (mui_textedit_control_t *)c;
|
||||
*glyph_start = te->sel.start;
|
||||
*glyph_end = te->sel.end;
|
||||
}
|
||||
|
||||
void
|
||||
mui_textedit_set_text(
|
||||
mui_control_t * c,
|
||||
const char * text)
|
||||
{
|
||||
mui_textedit_control_t *te = (mui_textedit_control_t *)c;
|
||||
mui_utf8_clear(&te->text);
|
||||
int tl = strlen(text);
|
||||
mui_utf8_realloc(&te->text, tl + 1);
|
||||
memcpy(te->text.e, text, tl + 1);
|
||||
/*
|
||||
* Note, the text.count *counts the terminating zero*
|
||||
*/
|
||||
te->text.count = tl + 1;
|
||||
if (!te->font)
|
||||
te->font = mui_font_find(c->win->ui, "main");
|
||||
_mui_textedit_refresh_measure(te);
|
||||
}
|
||||
|
||||
uint
|
||||
mui_textedit_get_text(
|
||||
mui_control_t * c,
|
||||
char * text,
|
||||
uint max)
|
||||
{
|
||||
mui_textedit_control_t *te = (mui_textedit_control_t *)c;
|
||||
uint tl = te->text.count - 1;
|
||||
if (tl > max)
|
||||
tl = max;
|
||||
memcpy(text, te->text.e, tl);
|
||||
text[tl] = 0;
|
||||
return tl;
|
||||
}
|
185
libmui/src/mui_cdef_te_keys.c
Normal file
185
libmui/src/mui_cdef_te_keys.c
Normal file
@ -0,0 +1,185 @@
|
||||
/*
|
||||
* mui_cdef_te_keys.c
|
||||
*
|
||||
* Copyright (C) 2024 Michel Pollet <buserror@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include "mui.h"
|
||||
#include "mui_cdef_te_priv.h"
|
||||
|
||||
|
||||
/*
|
||||
██ ██ ███████ ██ ██ ██████ ██████ █████ ██████ ██████
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
█████ █████ ████ ██████ ██ ██ ███████ ██████ ██ ██
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ███████ ██ ██████ ██████ ██ ██ ██ ██ ██████
|
||||
*/
|
||||
bool
|
||||
_mui_textedit_key(
|
||||
struct mui_control_t * c,
|
||||
mui_event_t * ev)
|
||||
{
|
||||
mui_textedit_control_t *te = (mui_textedit_control_t *)c;
|
||||
|
||||
mui_glyph_line_array_t * me = &te->measure;
|
||||
if (ev->modifiers & MUI_MODIFIER_CTRL) {
|
||||
switch (ev->key.key) {
|
||||
case 'T': {
|
||||
te->trace = !te->trace;
|
||||
printf("TRACE %s\n", te->trace ? "ON" : "OFF");
|
||||
} break;
|
||||
case 'D': {// dump text status and measures lines
|
||||
printf("Text:\n'%s'\n", te->text.e);
|
||||
printf("Text count: %d\n", te->text.count);
|
||||
printf("Text measure: %d\n", me->count);
|
||||
for (uint i = 0; i < me->count; i++) {
|
||||
mui_glyph_array_t * line = &me->e[i];
|
||||
printf(" line %d: %d\n", i, line->count);
|
||||
for (uint j = 0; j < line->count; j++) {
|
||||
mui_glyph_t * g = &line->e[j];
|
||||
printf(" %3d: %04x:%c x:%3f w:%3d\n",
|
||||
j, te->text.e[g->pos],
|
||||
te->text.e[g->pos] < ' ' ?
|
||||
'.' : te->text.e[g->pos],
|
||||
g->x, g->w);
|
||||
}
|
||||
}
|
||||
te->flags |= MUI_TEXT_DEBUG;
|
||||
} break;
|
||||
case 'a': {
|
||||
printf("Select all: %d\n", te->text.count-1);
|
||||
_mui_textedit_select_signed(te, 0, te->text.count-1);
|
||||
} break;
|
||||
case 'c': {
|
||||
if (te->sel.start != te->sel.end) {
|
||||
uint32_t start = _mui_glyph_to_byte_offset(me, te->sel.start);
|
||||
uint32_t end = _mui_glyph_to_byte_offset(me, te->sel.end);
|
||||
mui_clipboard_set(c->win->ui,
|
||||
te->text.e + start, end - start);
|
||||
}
|
||||
} break;
|
||||
case 'x': {
|
||||
if (te->sel.start != te->sel.end) {
|
||||
uint32_t start = _mui_glyph_to_byte_offset(me, te->sel.start);
|
||||
uint32_t end = _mui_glyph_to_byte_offset(me, te->sel.end);
|
||||
mui_clipboard_set(c->win->ui,
|
||||
te->text.e + start, end - start);
|
||||
_mui_textedit_sel_delete(te, true, true);
|
||||
}
|
||||
} break;
|
||||
case 'v': {
|
||||
uint32_t len;
|
||||
const uint8_t * clip = mui_clipboard_get(c->win->ui, &len);
|
||||
if (clip) {
|
||||
if (te->sel.start != te->sel.end)
|
||||
_mui_textedit_sel_delete(te, true, true);
|
||||
mui_utf8_insert(&te->text,
|
||||
_mui_glyph_to_byte_offset(me, te->sel.start),
|
||||
clip, len);
|
||||
_mui_textedit_refresh_measure(te);
|
||||
_mui_textedit_select_signed(te,
|
||||
te->sel.start + len, te->sel.start + len);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
switch (ev->key.key) {
|
||||
case MUI_KEY_UP: {
|
||||
uint line, index;
|
||||
_mui_glyph_to_line_index(me, te->sel.start, &line, &index);
|
||||
if (line > 0) {
|
||||
uint pos = _mui_line_index_to_glyph(me, line-1, index);
|
||||
if (ev->modifiers & MUI_MODIFIER_SHIFT) {
|
||||
_mui_textedit_select_signed(te, te->sel.start, pos);
|
||||
} else {
|
||||
_mui_textedit_select_signed(te, pos, pos);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case MUI_KEY_DOWN: {
|
||||
uint line, index;
|
||||
_mui_glyph_to_line_index(me, te->sel.start, &line, &index);
|
||||
if (line < me->count-1) {
|
||||
uint pos = _mui_line_index_to_glyph(me, line+1, index);
|
||||
if (ev->modifiers & MUI_MODIFIER_SHIFT) {
|
||||
_mui_textedit_select_signed(te, te->sel.start, pos);
|
||||
} else {
|
||||
_mui_textedit_select_signed(te, pos, pos);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case MUI_KEY_LEFT: {
|
||||
if (ev->modifiers & MUI_MODIFIER_SHIFT) {
|
||||
_mui_textedit_select_signed(te, te->sel.start - 1, te->sel.end);
|
||||
} else {
|
||||
if (te->sel.start == te->sel.end)
|
||||
_mui_textedit_select_signed(te, te->sel.start - 1, te->sel.start - 1);
|
||||
else
|
||||
_mui_textedit_select_signed(te, te->sel.start, te->sel.start);
|
||||
}
|
||||
} break;
|
||||
case MUI_KEY_RIGHT: {
|
||||
if (ev->modifiers & MUI_MODIFIER_SHIFT) {
|
||||
_mui_textedit_select_signed(te, te->sel.start, te->sel.end + 1);
|
||||
} else {
|
||||
if (te->sel.start == te->sel.end)
|
||||
_mui_textedit_select_signed(te, te->sel.start + 1, te->sel.start + 1);
|
||||
else
|
||||
_mui_textedit_select_signed(te, te->sel.end, te->sel.end);
|
||||
}
|
||||
} break;
|
||||
case MUI_KEY_BACKSPACE: {
|
||||
if (te->sel.start == te->sel.end) {
|
||||
if (te->sel.start > 0) {
|
||||
mui_utf8_delete(&te->text,
|
||||
_mui_glyph_to_byte_offset(me, te->sel.start - 1),
|
||||
1);
|
||||
_mui_textedit_refresh_measure(te);
|
||||
_mui_textedit_select_signed(te, te->sel.start - 1, te->sel.start - 1);
|
||||
}
|
||||
} else {
|
||||
_mui_textedit_sel_delete(te, true, true);
|
||||
}
|
||||
} break;
|
||||
case MUI_KEY_DELETE: {
|
||||
if (te->sel.start == te->sel.end) {
|
||||
if (te->sel.start < te->text.count-1) {
|
||||
mui_utf8_delete(&te->text,
|
||||
_mui_glyph_to_byte_offset(me, te->sel.start), 1);
|
||||
_mui_textedit_refresh_measure(te);
|
||||
_mui_textedit_select_signed(te, te->sel.start, te->sel.start);
|
||||
}
|
||||
} else {
|
||||
_mui_textedit_sel_delete(te, true, true);
|
||||
}
|
||||
} break;
|
||||
case MUI_KEY_TAB: {
|
||||
mui_control_switch_focus(c->win,
|
||||
ev->modifiers & MUI_MODIFIER_SHIFT ? -1 : 0);
|
||||
} break;
|
||||
default:
|
||||
printf("%s key 0x%x\n", __func__, ev->key.key);
|
||||
if (ev->key.key == 13 && !(te->flags & MUI_CONTROL_TEXTEDIT_VERTICAL))
|
||||
return false;
|
||||
if (ev->key.key == 13 ||
|
||||
(ev->key.key >= 32 && ev->key.key < 127)) {
|
||||
if (te->sel.start != te->sel.end) {
|
||||
_mui_textedit_sel_delete(te, false, false);
|
||||
_mui_textedit_select_signed(te, te->sel.start, te->sel.start);
|
||||
}
|
||||
uint8_t k = ev->key.key;
|
||||
mui_utf8_insert(&te->text,
|
||||
_mui_glyph_to_byte_offset(me, te->sel.start), &k, 1);
|
||||
_mui_textedit_refresh_measure(te);
|
||||
_mui_textedit_select_signed(te,
|
||||
te->sel.start + 1, te->sel.start + 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
_mui_textedit_show_carret(te);
|
||||
return true;
|
||||
}
|
236
libmui/src/mui_cdef_te_metrics.c
Normal file
236
libmui/src/mui_cdef_te_metrics.c
Normal file
@ -0,0 +1,236 @@
|
||||
/*
|
||||
* mui_cdef_te_metrics.c
|
||||
*
|
||||
* Copyright (C) 2024 Michel Pollet <buserror@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "mui.h"
|
||||
#include "mui_cdef_te_priv.h"
|
||||
|
||||
|
||||
#define D(_w) ; // _w
|
||||
|
||||
/*
|
||||
███ ███ ███████ █████ ███████ ██ ██ ██████ ███████
|
||||
████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ████ ██ █████ ███████ ███████ ██ ██ ██████ █████
|
||||
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
██ ██ ███████ ██ ██ ███████ ██████ ██ ██ ███████
|
||||
*/
|
||||
/*
|
||||
* Calculate the 3 rectangles that represent the graphical selection.
|
||||
* The 'start' is the first line of the selection, or the position of the
|
||||
* carret if the selection is empty.
|
||||
* The other two are 'optional' (they can be empty), and represent the last
|
||||
* line of the selection, and the body of the selection that is the rectangle
|
||||
* between the first and last line.
|
||||
*/
|
||||
int
|
||||
_mui_make_sel_rects(
|
||||
mui_glyph_line_array_t * measure,
|
||||
mui_font_t * font,
|
||||
mui_sel_t * sel,
|
||||
c2_rect_t frame)
|
||||
{
|
||||
if (!measure->count)
|
||||
return -1;
|
||||
sel->last = sel->first = sel->body = (c2_rect_t) {};
|
||||
uint start_line, start_index;
|
||||
uint end_line, end_index;
|
||||
_mui_glyph_to_line_index(measure, sel->start, &start_line, &start_index);
|
||||
_mui_glyph_to_line_index(measure, sel->end, &end_line, &end_index);
|
||||
mui_glyph_array_t * line = &measure->e[start_line];
|
||||
|
||||
if (start_line == end_line) {
|
||||
// single line selection
|
||||
sel->first = (c2_rect_t) {
|
||||
.l = frame.l + line->e[start_index].x,
|
||||
.t = frame.t + line->t,
|
||||
.r = frame.l + line->e[end_index].x,
|
||||
.b = frame.t + line->b,
|
||||
};
|
||||
return 0;
|
||||
}
|
||||
// first line
|
||||
sel->first = (c2_rect_t) {
|
||||
.l = frame.l + line->e[start_index].x, .t = frame.t + line->t,
|
||||
.r = frame.r, .b = frame.t + line->b,
|
||||
};
|
||||
// last line
|
||||
line = &measure->e[end_line];
|
||||
sel->last = (c2_rect_t) {
|
||||
.l = frame.l, .t = frame.t + line->t,
|
||||
.r = frame.l + line->e[end_index].x, .b = frame.t + line->b,
|
||||
};
|
||||
// body
|
||||
sel->body = (c2_rect_t) {
|
||||
.l = frame.l, .t = sel->first.b,
|
||||
.r = frame.r, .b = sel->last.t,
|
||||
};
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Refresh the whole selection (or around the carret selection) */
|
||||
void
|
||||
_mui_textedit_refresh_sel(
|
||||
mui_textedit_control_t * te,
|
||||
mui_sel_t * sel)
|
||||
{
|
||||
if (!sel)
|
||||
sel = &te->sel;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
c2_rect_t r = te->sel.e[i];
|
||||
if (i == 0 && te->sel.start == te->sel.end) {
|
||||
c2_rect_inset(&r, -1, -1);
|
||||
// printf("refresh_sel: carret %s\n", c2_rect_as_str(&r));
|
||||
}
|
||||
if (!c2_rect_isempty(&r))
|
||||
_mui_textedit_inval(te, r);
|
||||
}
|
||||
}
|
||||
|
||||
/* this makes sure the text is always visible in the frame */
|
||||
void
|
||||
_mui_textedit_clamp_text_frame(
|
||||
mui_textedit_control_t * te)
|
||||
{
|
||||
c2_rect_t f = te->control.frame;
|
||||
c2_rect_offset(&f, -f.l, -f.t);
|
||||
if (te->flags & MUI_CONTROL_TEXTBOX_FRAME)
|
||||
c2_rect_inset(&f, te->margin.x, te->margin.y);
|
||||
c2_rect_t old = te->text_content;
|
||||
te->text_content.r = te->text_content.l + te->measure.margin_right;
|
||||
te->text_content.b = te->text_content.t + te->measure.height;
|
||||
D(printf(" %s %s / %3dx%3d\n", __func__,
|
||||
c2_rect_as_str(&te->text_content),
|
||||
c2_rect_width(&f), c2_rect_height(&f));)
|
||||
if (te->text_content.b < c2_rect_height(&f))
|
||||
c2_rect_offset(&te->text_content, 0,
|
||||
c2_rect_height(&f) - te->text_content.b);
|
||||
if (te->text_content.t > f.t)
|
||||
c2_rect_offset(&te->text_content, 0, f.t - te->text_content.t);
|
||||
if (te->text_content.r < c2_rect_width(&f))
|
||||
c2_rect_offset(&te->text_content,
|
||||
c2_rect_width(&f) - te->text_content.r, 0);
|
||||
if (te->text_content.l > f.l)
|
||||
c2_rect_offset(&te->text_content, f.l - te->text_content.l, 0);
|
||||
if (c2_rect_equal(&te->text_content, &old))
|
||||
return;
|
||||
D(printf(" clamped TE from %s to %s\n", c2_rect_as_str(&old),
|
||||
c2_rect_as_str(&te->text_content));)
|
||||
mui_control_inval(&te->control);
|
||||
}
|
||||
|
||||
/* This scrolls the view following the carret, used when typing.
|
||||
* This doesn't check for out of bounds, but the clamping should
|
||||
* have made sure the text is always visible. */
|
||||
void
|
||||
_mui_textedit_ensure_carret_visible(
|
||||
mui_textedit_control_t * te)
|
||||
{
|
||||
c2_rect_t f = te->control.frame;
|
||||
// c2_rect_offset(&f, -f.l, -f.t);
|
||||
if (te->flags & MUI_CONTROL_TEXTBOX_FRAME)
|
||||
c2_rect_inset(&f, te->margin.x, te->margin.y);
|
||||
if (te->sel.start != te->sel.end)
|
||||
return;
|
||||
c2_rect_t old = te->text_content;
|
||||
c2_rect_t r = te->sel.first;
|
||||
D(printf("%s carret %s frame %s\n", __func__,
|
||||
c2_rect_as_str(&r), c2_rect_as_str(&f));)
|
||||
c2_rect_offset(&r, -te->text_content.l, -te->text_content.t);
|
||||
if (r.r < f.l) {
|
||||
D(printf(" moved TE LEFT %d\n", -(f.l - r.r));)
|
||||
c2_rect_offset(&te->text_content, -(f.l - r.l), 0);
|
||||
}
|
||||
if (r.l > f.r) {
|
||||
D(printf(" moved TE RIGHT %d\n", -(r.l - f.r));)
|
||||
c2_rect_offset(&te->text_content, -(r.l - f.r), 0);
|
||||
}
|
||||
if (r.t < f.t)
|
||||
c2_rect_offset(&te->text_content, 0, r.t - f.t);
|
||||
if (r.b > f.b)
|
||||
c2_rect_offset(&te->text_content, 0, r.b - f.b);
|
||||
if (c2_rect_equal(&te->text_content, &old))
|
||||
return;
|
||||
D(printf(" moved TE from %s to %s\n", c2_rect_as_str(&old),
|
||||
c2_rect_as_str(&te->text_content));)
|
||||
_mui_textedit_clamp_text_frame(te);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is to be called when the text changes, or the frame (width) changes
|
||||
*/
|
||||
void
|
||||
_mui_textedit_refresh_measure(
|
||||
mui_textedit_control_t * te)
|
||||
{
|
||||
c2_rect_t f = te->control.frame;
|
||||
c2_rect_offset(&f, -f.l, -f.t);
|
||||
if (te->flags & MUI_CONTROL_TEXTBOX_FRAME)
|
||||
c2_rect_inset(&f, te->margin.x, te->margin.y);
|
||||
if (!(te->flags & MUI_CONTROL_TEXTEDIT_VERTICAL))
|
||||
f.r = 0x7fff; // make it very large, we don't want wrapping.
|
||||
|
||||
mui_glyph_line_array_t new_measure = {};
|
||||
|
||||
mui_font_measure(te->font, f,
|
||||
(const char*)te->text.e, te->text.count-1,
|
||||
&new_measure, te->flags);
|
||||
|
||||
f = te->control.frame;
|
||||
if (te->flags & MUI_CONTROL_TEXTBOX_FRAME)
|
||||
c2_rect_inset(&f, te->margin.x, te->margin.y);
|
||||
// Refresh the lines that have changed. Perhaps all of them did,
|
||||
// doesn't matter, but it's nice to avoid redrawing the whole text
|
||||
// when someone is typing.
|
||||
for (uint i = 0; i < new_measure.count && i < te->measure.count; i++) {
|
||||
if (i >= te->measure.count) {
|
||||
c2_rect_t r = f;
|
||||
r.t += new_measure.e[i].t;
|
||||
r.b = r.t + new_measure.e[i].b;
|
||||
r.r = new_measure.e[i].x + new_measure.e[i].w;
|
||||
_mui_textedit_inval(te, r);
|
||||
} else if (i >= new_measure.count) {
|
||||
c2_rect_t r = f;
|
||||
r.t += te->measure.e[i].t;
|
||||
r.b = r.t + te->measure.e[i].b;
|
||||
r.r = te->measure.e[i].x + te->measure.e[i].w;
|
||||
_mui_textedit_inval(te, r);
|
||||
} else {
|
||||
int dirty = 0;
|
||||
// unsure if this could happen, but let's be safe --
|
||||
// technically we should refresh BOTH rectangles (old, new)
|
||||
if (new_measure.e[i].t != te->measure.e[i].t ||
|
||||
new_measure.e[i].b != te->measure.e[i].b) {
|
||||
dirty = 1;
|
||||
} else if (new_measure.e[i].x != te->measure.e[i].x ||
|
||||
new_measure.e[i].count != te->measure.e[i].count ||
|
||||
new_measure.e[i].w != te->measure.e[i].w)
|
||||
dirty = 1;
|
||||
else {
|
||||
for (uint x = 0; x < new_measure.e[i].count; x++) {
|
||||
if (new_measure.e[i].e[x].glyph != te->measure.e[i].e[x].glyph ||
|
||||
new_measure.e[i].e[x].x != te->measure.e[i].e[x].x ||
|
||||
new_measure.e[i].e[x].w != te->measure.e[i].e[x].w) {
|
||||
dirty = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dirty) {
|
||||
c2_rect_t r = f;
|
||||
r.t += new_measure.e[i].t;
|
||||
r.b = r.t + new_measure.e[i].b;
|
||||
r.r = new_measure.e[i].x + new_measure.e[i].w;
|
||||
_mui_textedit_inval(te, r);
|
||||
}
|
||||
}
|
||||
}
|
||||
mui_font_measure_clear(&te->measure);
|
||||
te->measure = new_measure;
|
||||
_mui_textedit_clamp_text_frame(te);
|
||||
}
|
141
libmui/src/mui_cdef_te_priv.h
Normal file
141
libmui/src/mui_cdef_te_priv.h
Normal file
@ -0,0 +1,141 @@
|
||||
/*
|
||||
* mui_cdef_te_priv.h
|
||||
*
|
||||
* Copyright (C) 2024 Michel Pollet <buserror@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "mui.h"
|
||||
|
||||
|
||||
/*
|
||||
* This describes a text edit action, either we insert some text at some position,
|
||||
* or we delete some text at some position.
|
||||
* These actions are queued in a TAILQ, so we can undo/redo them.
|
||||
* The text is UTF8, and the position is a BYTE index in the text (not a glyph).
|
||||
*
|
||||
* We preallocate a fixed number of actions, and when we reach the limit, we
|
||||
* start reusing the oldest ones. This limits the number of undo/redo actions
|
||||
* to something sensible.
|
||||
*/
|
||||
typedef struct mui_te_action_t {
|
||||
TAILQ_ENTRY(mui_te_action_t) self;
|
||||
uint insert : 1; // if not insert, its a delete
|
||||
uint32_t position, length;
|
||||
mui_utf8_t text;
|
||||
} mui_te_action_t;
|
||||
|
||||
// action queue
|
||||
typedef TAILQ_HEAD(mui_te_action_queue_t, mui_te_action_t) mui_te_action_queue_t;
|
||||
|
||||
/*
|
||||
* This describes the selection in the text-edit, it can either be a carret,
|
||||
* or a selection of text. The selection is kept as a start and end glyph index,
|
||||
* and the drawing code calculates the rectangles for the selection.
|
||||
*/
|
||||
typedef struct mui_sel_t {
|
||||
uint carret: 1; // carret is visible (if sel.start == end)
|
||||
uint start, end; // glyph index in text
|
||||
// rectangles for the first partial line, the body,
|
||||
// and the last partial line. All of them can be empty
|
||||
union {
|
||||
struct {
|
||||
c2_rect_t first, body, last;
|
||||
};
|
||||
c2_rect_t e[3];
|
||||
};
|
||||
} mui_sel_t;
|
||||
|
||||
typedef struct mui_textedit_control_t {
|
||||
mui_control_t control;
|
||||
uint trace : 1; // debug trace
|
||||
uint32_t flags; // display flags
|
||||
mui_sel_t sel;
|
||||
mui_font_t * font;
|
||||
mui_utf8_t text;
|
||||
mui_glyph_line_array_t measure;
|
||||
c2_pt_t margin;
|
||||
c2_rect_t text_content;
|
||||
struct {
|
||||
uint start, end;
|
||||
} click;
|
||||
uint selecting_mode;
|
||||
} mui_textedit_control_t;
|
||||
|
||||
|
||||
bool
|
||||
_mui_textedit_key(
|
||||
struct mui_control_t * c,
|
||||
mui_event_t * ev);
|
||||
|
||||
/* this 'forces' the carret to be visible, used when typing */
|
||||
void
|
||||
_mui_textedit_show_carret(
|
||||
mui_textedit_control_t * te);
|
||||
/* this one allows passing -1 etc, which is handy of cursor movement */
|
||||
void
|
||||
_mui_textedit_select_signed(
|
||||
mui_textedit_control_t * te,
|
||||
int glyph_start,
|
||||
int glyph_end);
|
||||
/* Refresh the whole selection (or around the carret selection) */
|
||||
void
|
||||
_mui_textedit_refresh_sel(
|
||||
mui_textedit_control_t * te,
|
||||
mui_sel_t * sel);
|
||||
uint
|
||||
_mui_glyph_to_byte_offset(
|
||||
mui_glyph_line_array_t * measure,
|
||||
uint glyph_pos);
|
||||
/* Return the glyph position in the text for line number and index in line */
|
||||
uint
|
||||
_mui_line_index_to_glyph(
|
||||
mui_glyph_line_array_t * measure,
|
||||
uint line,
|
||||
uint index);
|
||||
|
||||
/* Return the line number, and glyph position in line a glyph index */
|
||||
int
|
||||
_mui_glyph_to_line_index(
|
||||
mui_glyph_line_array_t * measure,
|
||||
uint glyph_pos,
|
||||
uint * out_line,
|
||||
uint * out_line_index);
|
||||
|
||||
void
|
||||
_mui_textedit_sel_delete(
|
||||
mui_textedit_control_t * te,
|
||||
bool re_measure,
|
||||
bool reset_sel);
|
||||
int
|
||||
_mui_make_sel_rects(
|
||||
mui_glyph_line_array_t * measure,
|
||||
mui_font_t * font,
|
||||
mui_sel_t * sel,
|
||||
c2_rect_t frame);
|
||||
/* This scrolls the view following the carret, used when typing.
|
||||
* This doesn't check for out of bounds, but the clamping should
|
||||
* have made sure the text is always visible. */
|
||||
void
|
||||
_mui_textedit_ensure_carret_visible(
|
||||
mui_textedit_control_t * te);
|
||||
/* this makes sure the text is always visible in the frame */
|
||||
void
|
||||
_mui_textedit_clamp_text_frame(
|
||||
mui_textedit_control_t * te);
|
||||
|
||||
void
|
||||
_mui_textedit_refresh_measure(
|
||||
mui_textedit_control_t * te);
|
||||
/*
|
||||
* Rectangles passed here are in TEXT coordinates.
|
||||
* which means they are already offset by margin.x, margin.y
|
||||
* and the text_content.tl.x, text_content.tl.y
|
||||
*/
|
||||
void
|
||||
_mui_textedit_inval(
|
||||
mui_textedit_control_t * te,
|
||||
c2_rect_t r);
|
191
libmui/src/mui_control_group.c
Normal file
191
libmui/src/mui_control_group.c
Normal file
@ -0,0 +1,191 @@
|
||||
/*
|
||||
* mui_control_group.c
|
||||
*
|
||||
* Copyright (C) 2024 Michel Pollet <buserror@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "mui.h"
|
||||
#include "mui_priv.h"
|
||||
|
||||
|
||||
void
|
||||
mui_controls_init(
|
||||
mui_controls_t * group)
|
||||
{
|
||||
TAILQ_INIT(&group->controls);
|
||||
}
|
||||
|
||||
void
|
||||
mui_control_group_init(
|
||||
struct mui_control_group_t * group,
|
||||
mui_controls_t * attach)
|
||||
{
|
||||
group->flags.hidden = false;
|
||||
TAILQ_INIT(&group->controls);
|
||||
if (attach)
|
||||
TAILQ_INSERT_TAIL(&attach->controls, group, self);
|
||||
}
|
||||
|
||||
struct mui_control_t *
|
||||
mui_control_group_first(
|
||||
struct mui_control_group_t * group,
|
||||
mui_controls_flags_e flags)
|
||||
{
|
||||
if (!group)
|
||||
return NULL;
|
||||
bool all = flags == MUI_CONTROLS_ALL;
|
||||
struct mui_control_t * c = TAILQ_FIRST(&group->controls);
|
||||
while (c) {
|
||||
if (all || !c->flags.hidden)
|
||||
return c;
|
||||
c = TAILQ_NEXT(c, self);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct mui_control_t *
|
||||
mui_control_group_last(
|
||||
struct mui_control_group_t * group,
|
||||
mui_controls_flags_e flags)
|
||||
{
|
||||
if (!group)
|
||||
return NULL;
|
||||
bool all = flags == MUI_CONTROLS_ALL;
|
||||
struct mui_control_t * c = TAILQ_LAST(&group->controls, controls);
|
||||
while (c) {
|
||||
if (all || !c->flags.hidden)
|
||||
return c;
|
||||
c = TAILQ_PREV(c, controls, self);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct mui_control_t *
|
||||
mui_control_group_next(
|
||||
struct mui_control_t * c,
|
||||
mui_controls_flags_e flags)
|
||||
{
|
||||
if (!c)
|
||||
return NULL;
|
||||
bool all = flags == MUI_CONTROLS_ALL;
|
||||
struct mui_control_t * next = TAILQ_NEXT(c, self);
|
||||
while (next) {
|
||||
if (all || !next->flags.hidden)
|
||||
return next;
|
||||
next = TAILQ_NEXT(next, self);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct mui_control_t *
|
||||
mui_control_group_prev(
|
||||
struct mui_control_t * c,
|
||||
mui_controls_flags_e flags)
|
||||
{
|
||||
if (!c)
|
||||
return NULL;
|
||||
bool all = flags == MUI_CONTROLS_ALL;
|
||||
struct mui_control_t * prev = TAILQ_PREV(c, controls, self);
|
||||
while (prev) {
|
||||
if (all || !prev->flags.hidden)
|
||||
return prev;
|
||||
prev = TAILQ_PREV(prev, controls, self);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct mui_control_t *
|
||||
mui_controls_first(
|
||||
mui_controls_t * group,
|
||||
mui_controls_flags_e flags)
|
||||
{
|
||||
if (!group)
|
||||
return NULL;
|
||||
bool all = flags == MUI_CONTROLS_ALL;
|
||||
struct mui_control_group_t * g;
|
||||
TAILQ_FOREACH(g, &group->controls, self) {
|
||||
if (!all && g->flags.hidden)
|
||||
continue;
|
||||
mui_control_t * c = mui_control_group_first(g, flags);
|
||||
if (c)
|
||||
return c;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct mui_control_t *
|
||||
mui_controls_last(
|
||||
mui_controls_t * group,
|
||||
mui_controls_flags_e flags)
|
||||
{
|
||||
if (!group)
|
||||
return NULL;
|
||||
bool all = flags == MUI_CONTROLS_ALL;
|
||||
struct mui_control_group_t * g;
|
||||
TAILQ_FOREACH_REVERSE(g, &group->controls, list, self) {
|
||||
if (!all && g->flags.hidden)
|
||||
continue;
|
||||
mui_control_t * c = mui_control_group_last(g, flags);
|
||||
if (c)
|
||||
return c;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct mui_control_group_t *
|
||||
mui_controls_current_group(
|
||||
mui_controls_t * group)
|
||||
{
|
||||
if (!group)
|
||||
return NULL;
|
||||
return TAILQ_LAST(&group->controls, list);
|
||||
}
|
||||
|
||||
struct mui_control_t *
|
||||
mui_controls_next(
|
||||
struct mui_control_t * control,
|
||||
mui_controls_flags_e flags)
|
||||
{
|
||||
if (!control)
|
||||
return NULL;
|
||||
struct mui_control_group_t * g = control->group;
|
||||
if (!g)
|
||||
return NULL;
|
||||
struct mui_control_t * c = mui_control_group_next(control, flags);
|
||||
if (c)
|
||||
return c;
|
||||
do {
|
||||
g = TAILQ_NEXT(g, self);
|
||||
c = mui_control_group_first(g, flags);
|
||||
if (c)
|
||||
return c;
|
||||
} while (g);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct mui_control_t *
|
||||
mui_controls_prev(
|
||||
struct mui_control_t * control,
|
||||
mui_controls_flags_e flags)
|
||||
{
|
||||
if (!control)
|
||||
return NULL;
|
||||
struct mui_control_group_t * g = control->group;
|
||||
if (!g)
|
||||
return NULL;
|
||||
struct mui_control_t * c = TAILQ_PREV(control, controls, self);
|
||||
if (c)
|
||||
return c;
|
||||
do {
|
||||
g = TAILQ_PREV(g, list, self);
|
||||
c = mui_control_group_last(g, flags);
|
||||
if (c)
|
||||
return c;
|
||||
} while (g);
|
||||
return NULL;
|
||||
}
|
@ -70,10 +70,11 @@ mui_control_new(
|
||||
c->frame = frame;
|
||||
c->title = title ? strdup(title) : NULL;
|
||||
c->win = win;
|
||||
c->group = mui_controls_current_group(&win->controls);
|
||||
c->uid = uid;
|
||||
mui_refqueue_init(&c->refs);
|
||||
STAILQ_INIT(&c->actions);
|
||||
TAILQ_INSERT_TAIL(&win->controls, c, self);
|
||||
TAILQ_INSERT_TAIL(&c->group->controls, c, self);
|
||||
if (c->cdef)
|
||||
c->cdef(c, MUI_CDEF_INIT, NULL);
|
||||
// should we auto-focus the control? not sure..
|
||||
@ -100,11 +101,12 @@ mui_control_dispose(
|
||||
{
|
||||
if (!c)
|
||||
return;
|
||||
if (c->win) {
|
||||
TAILQ_REMOVE(&c->win->controls, c, self);
|
||||
if (c->win && c->group) {
|
||||
TAILQ_REMOVE(&c->group->controls, c, self);
|
||||
if (c->cdef)
|
||||
c->cdef(c, MUI_CDEF_DISPOSE, NULL);
|
||||
c->win = NULL;
|
||||
c->group = NULL;
|
||||
mui_control_dispose_actions(c);
|
||||
}
|
||||
if (mui_refqueue_dispose(&c->refs) != 0) {
|
||||
@ -139,12 +141,13 @@ mui_control_locate(
|
||||
{
|
||||
if (!win)
|
||||
return NULL;
|
||||
mui_control_t * c;
|
||||
TAILQ_FOREACH(c, &win->controls, self) {
|
||||
mui_control_t * c = mui_controls_first(&win->controls, MUI_CONTROLS_VISIBLE);
|
||||
while (c) {
|
||||
c2_rect_t f = c->frame;
|
||||
c2_rect_offset(&f, win->content.l, win->content.t);
|
||||
if (c2_rect_contains_pt(&f, &pt))
|
||||
return c;
|
||||
c = mui_controls_next(c, MUI_CONTROLS_VISIBLE);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@ -403,10 +406,11 @@ mui_control_get_by_id(
|
||||
{
|
||||
if (!win)
|
||||
return NULL;
|
||||
mui_control_t *c;
|
||||
TAILQ_FOREACH(c, &win->controls, self) {
|
||||
mui_control_t *c = mui_controls_first(&win->controls, MUI_CONTROLS_ALL);
|
||||
while (c) {
|
||||
if (c->uid == uid)
|
||||
return c;
|
||||
c = mui_controls_next(c, MUI_CONTROLS_ALL);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@ -451,20 +455,22 @@ mui_control_switch_focus(
|
||||
if (!win)
|
||||
return NULL;
|
||||
mui_control_t *c = win->control_focus.control;
|
||||
mui_controls_flags_e flags = MUI_CONTROLS_VISIBLE;
|
||||
if (!c)
|
||||
c = TAILQ_FIRST(&win->controls);
|
||||
c = mui_controls_first(&win->controls, flags);
|
||||
if (!c)
|
||||
return c;
|
||||
mui_control_t * start = c;
|
||||
do {
|
||||
c = dir > 0 ? TAILQ_NEXT(c, self) : TAILQ_PREV(c, controls, self);
|
||||
c = dir > 0 ?
|
||||
mui_controls_next(c, flags) : mui_controls_prev(c, flags);
|
||||
if (!c)
|
||||
c = dir > 0 ? TAILQ_FIRST(&win->controls) :
|
||||
TAILQ_LAST(&win->controls, controls);
|
||||
c = dir > 0 ? mui_controls_first(&win->controls, flags) :
|
||||
mui_controls_last(&win->controls, flags);
|
||||
if (c->cdef && c->cdef(c, MUI_CDEF_CAN_FOCUS, NULL))
|
||||
break;
|
||||
} while (c != start);
|
||||
mui_control_set_focus(c);
|
||||
printf("focus %4.4s %s\n", (char*)&c->type, c->title);
|
||||
// printf("%s %4.4s %s\n", __func__, (char*)&c->type, c->title);
|
||||
return c;
|
||||
}
|
||||
|
@ -14,19 +14,9 @@
|
||||
#define STB_TTC_IMPLEMENTATION
|
||||
#include "stb_ttc.h"
|
||||
|
||||
|
||||
//#ifndef __wasm__
|
||||
#define INCBIN_STYLE INCBIN_STYLE_SNAKE
|
||||
#define INCBIN_PREFIX mui_
|
||||
#include "incbin.h"
|
||||
|
||||
INCBIN(main_font, "fonts/Charcoal_mui.ttf");
|
||||
INCBIN(icon_font, "fonts/typicon.ttf");
|
||||
INCBIN(dingbat_font, "fonts/Dingbat.ttf");
|
||||
INCBIN(geneva_font, "fonts/Geneva.ttf");
|
||||
//#endif
|
||||
|
||||
#include "mui.h"
|
||||
#include "fonts/mui_main_font.h"
|
||||
#include "fonts/mui_icon_font.h"
|
||||
|
||||
// "Narrow style" reduces the advance by this factor
|
||||
// Not the 'space' characters are reduced even more (twice that)
|
||||
@ -81,14 +71,12 @@ mui_font_init(
|
||||
mui_t *ui)
|
||||
{
|
||||
// printf("%s: Loading fonts\n", __func__);
|
||||
#ifndef __wasm__
|
||||
mui_font_from_mem(ui, "main", 28,
|
||||
mui_main_font_data, mui_main_font_size);
|
||||
mui_main_font, mui_main_font_len);
|
||||
mui_font_from_mem(ui, "icon_large", 96,
|
||||
mui_icon_font_data, mui_icon_font_size);
|
||||
mui_icon_font, mui_icon_font_len);
|
||||
mui_font_from_mem(ui, "icon_small", 30,
|
||||
mui_icon_font_data, mui_icon_font_size);
|
||||
#endif
|
||||
mui_icon_font, mui_icon_font_len);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -324,7 +324,7 @@ mui_menubar_handle_mouse(
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
return inside;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -364,8 +364,8 @@ mui_menubar_handle_keydown(
|
||||
// non-zero, if would, highlight that menu (temporarily)
|
||||
// and call the action function witht that menu item. score!
|
||||
mui_window_t * win = &mbar->win;
|
||||
for (mui_control_t * c = TAILQ_FIRST(&win->controls);
|
||||
c; c = TAILQ_NEXT(c, self)) {
|
||||
mui_control_t * c = mui_controls_first(&win->controls, MUI_CONTROLS_ALL);
|
||||
for (; c; c = mui_controls_next(c, MUI_CONTROLS_ALL)) {
|
||||
if (c->type != MUI_CONTROL_MENUTITLE)
|
||||
continue;
|
||||
mui_menu_control_t * title = (mui_menu_control_t*)c;
|
||||
@ -603,7 +603,7 @@ mui_menubar_add_simple(
|
||||
int title_width = m.x1 - m.x0 + (main->size / 2);
|
||||
c2_rect_t title_rect = { .t = 2 };
|
||||
|
||||
mui_control_t * last = TAILQ_LAST(&win->controls, controls);
|
||||
mui_control_t * last = mui_controls_last(&win->controls, MUI_CONTROLS_ALL);
|
||||
if (last) {
|
||||
c2_rect_offset(&title_rect, last->frame.r, 0);
|
||||
} else
|
||||
@ -647,7 +647,7 @@ mui_menubar_add_menu(
|
||||
int title_width = c2_rect_width(&parts[MUI_MENUTITLE_PART_ALL]);
|
||||
c2_rect_t title_rect = { .t = 2 };
|
||||
|
||||
mui_control_t * last = TAILQ_LAST(&win->controls, controls);
|
||||
mui_control_t * last = mui_controls_last(&win->controls, MUI_CONTROLS_ALL);
|
||||
if (last) {
|
||||
c2_rect_offset(&title_rect, last->frame.r, 0);
|
||||
} else
|
||||
@ -696,8 +696,8 @@ mui_menubar_highlight(
|
||||
bool ignored )
|
||||
{
|
||||
// mui_menubar_t * mbar = (mui_menubar_t*)win;
|
||||
mui_control_t * c = NULL;
|
||||
TAILQ_FOREACH(c, &win->controls, self) {
|
||||
mui_control_t * c = mui_controls_first(&win->controls, MUI_CONTROLS_ALL);
|
||||
for (; c; c = mui_controls_next(c, MUI_CONTROLS_ALL)) {
|
||||
if (c->type == MUI_CONTROL_MENUTITLE ||
|
||||
mui_control_get_state(c)) {
|
||||
mui_control_set_state(c, 0);
|
||||
|
@ -30,6 +30,15 @@ IMPLEMENT_C_ARRAY(string_array);
|
||||
|
||||
#define MUI_STDF_MAX_SUFFIX 16
|
||||
|
||||
struct mui_stdfile_t;
|
||||
|
||||
typedef struct mui_stdnewfolder_t {
|
||||
mui_window_t win;
|
||||
mui_control_t * ok, *cancel;
|
||||
mui_control_t * save_name;
|
||||
struct mui_stdfile_t * std;
|
||||
} mui_stdnewfolder_t;
|
||||
|
||||
typedef struct mui_stdfile_t {
|
||||
mui_window_t win;
|
||||
mui_control_t * ok, *cancel, *home, *root;
|
||||
@ -46,6 +55,7 @@ typedef struct mui_stdfile_t {
|
||||
struct {
|
||||
mui_control_t *save_name;
|
||||
mui_control_t *create_folder;
|
||||
mui_stdnewfolder_t *new_folder_window;
|
||||
} save;
|
||||
#ifdef MUI_HAS_REGEXP
|
||||
regex_t re;
|
||||
@ -64,6 +74,11 @@ enum {
|
||||
MUI_STD_FILE_PART_NEW,
|
||||
MUI_STD_FILE_PART_SAVE_NAME,
|
||||
MUI_STD_FILE_PART_COUNT,
|
||||
// for the new folder dialog
|
||||
MUI_STD_NEWF_PART_OK = 20,
|
||||
MUI_STD_NEWF_PART_CANCEL,
|
||||
MUI_STD_NEWF_PART_SAVE_NAME,
|
||||
MUI_STD_NEWF_PART_COUNT,
|
||||
};
|
||||
|
||||
static int
|
||||
@ -336,6 +351,118 @@ _mui_stdfile_load_pref(
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int
|
||||
_mui_std_newf_control_action(
|
||||
mui_control_t * c,
|
||||
void * cb_param,
|
||||
uint32_t what,
|
||||
void * param)
|
||||
{
|
||||
mui_stdnewfolder_t * newf = cb_param;
|
||||
mui_stdfile_t * std = newf->std;
|
||||
switch (c->uid) {
|
||||
case MUI_STD_NEWF_PART_OK: {
|
||||
char name[64];
|
||||
mui_textedit_get_text(newf->save_name, name, sizeof(name));
|
||||
printf("New folder: %s\n", name);
|
||||
char * full_path = NULL;
|
||||
asprintf(&full_path, "%s/%s", std->current_path, name);
|
||||
// check new path is not already there
|
||||
if (_mii_stdfile_check_dir(full_path) == 0) {
|
||||
char * msg = NULL;
|
||||
asprintf(&msg, "%s\n%s", full_path,
|
||||
"Already exists");
|
||||
mui_alert(std->win.ui, C2_PT(0,0),
|
||||
"Could not create folder",
|
||||
msg,
|
||||
MUI_ALERT_FLAG_OK);
|
||||
free(full_path);
|
||||
break;
|
||||
}
|
||||
if (mkdir(full_path, 0777) == 0) {
|
||||
_mui_stdfile_populate(std, full_path);
|
||||
mui_window_dispose(c->win);
|
||||
} else {
|
||||
char * msg = NULL;
|
||||
asprintf(&msg, "%s\n%s", full_path,
|
||||
strerror(errno));
|
||||
mui_alert(std->win.ui, C2_PT(0,0),
|
||||
"Could not create folder",
|
||||
msg,
|
||||
MUI_ALERT_FLAG_OK);
|
||||
}
|
||||
free(full_path);
|
||||
} break;
|
||||
case MUI_STD_NEWF_PART_CANCEL:
|
||||
mui_window_action(c->win,
|
||||
MUI_STDF_ACTION_CANCEL, NULL);
|
||||
mui_window_dispose(c->win);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static mui_window_t *
|
||||
_mui_std_newf_window(
|
||||
mui_stdfile_t * std)
|
||||
{
|
||||
c2_pt_t where = C2_PT(0, 0);
|
||||
mui_t * ui = std->win.ui;
|
||||
float base_size = mui_font_find(ui, "main")->size;
|
||||
float margin = base_size * 0.7;
|
||||
|
||||
c2_rect_t wpos = C2_RECT_WH(where.x, where.y, 400, 175);
|
||||
if (where.x == 0 && where.y == 0)
|
||||
c2_rect_offset(&wpos,
|
||||
(ui->screen_size.x / 2) - (c2_rect_width(&wpos) / 2),
|
||||
(ui->screen_size.y * 0.4) - (c2_rect_height(&wpos) / 2));
|
||||
|
||||
mui_window_t *w = mui_window_create(std->win.ui,
|
||||
wpos,
|
||||
NULL, MUI_WINDOW_LAYER_MODAL + 1,
|
||||
"New Folder", sizeof(mui_stdnewfolder_t));
|
||||
mui_stdnewfolder_t * newf = (mui_stdnewfolder_t *)w;
|
||||
// mui_window_set_action(w, _mui_stdfile_window_action, NULL);
|
||||
mui_control_t * c = NULL;
|
||||
|
||||
c2_rect_t cf = C2_RECT_WH(0, 0, 120, 40);
|
||||
c2_rect_left_of(&cf, c2_rect_width(&w->content), margin);
|
||||
c2_rect_top_of(&cf, c2_rect_height(&w->content), margin);
|
||||
newf->ok = c = mui_button_new(w,
|
||||
cf, MUI_BUTTON_STYLE_DEFAULT,
|
||||
"Create", MUI_STD_NEWF_PART_OK);
|
||||
c2_rect_left_of(&cf, cf.l, margin);
|
||||
newf->cancel = c = mui_button_new(w,
|
||||
cf, MUI_BUTTON_STYLE_NORMAL,
|
||||
"Cancel", MUI_STD_NEWF_PART_CANCEL);
|
||||
c2_rect_top_of(&cf, cf.t, margin);
|
||||
|
||||
newf->ok->key_equ = MUI_KEY_EQU(0, 13); // return
|
||||
newf->cancel->key_equ = MUI_KEY_EQU(0, 27); // ESC
|
||||
|
||||
// full width
|
||||
cf.l = margin;
|
||||
cf.r = c2_rect_width(&w->content) - margin;
|
||||
cf.b = cf.t + 35;
|
||||
newf->save_name = c = mui_textedit_control_new(w,
|
||||
cf, MUI_CONTROL_TEXTBOX_FRAME);
|
||||
c->uid = MUI_STD_NEWF_PART_SAVE_NAME;
|
||||
mui_textedit_set_text(c, "Untitled Folder");
|
||||
mui_textedit_set_selection(c, 0, 255);
|
||||
c = mui_controls_first(&w->controls, MUI_CONTROLS_ALL);
|
||||
for (; c; c = mui_controls_next(c, MUI_CONTROLS_ALL)) {
|
||||
if (mui_control_get_uid(c))
|
||||
mui_control_set_action(c, _mui_std_newf_control_action, newf);
|
||||
}
|
||||
std->save.new_folder_window = newf;
|
||||
newf->std = std;
|
||||
// c2_rect_top_of(&cf, cf.t, 10);
|
||||
// mui_window_show(w);
|
||||
return w;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
_mui_stdfile_window_action(
|
||||
mui_window_t * win,
|
||||
@ -470,10 +597,17 @@ _mui_stdfile_control_action(
|
||||
_mui_stdfile_populate(std, items[idx].title);
|
||||
}
|
||||
break;
|
||||
case MUI_STD_FILE_PART_NEW: {
|
||||
_mui_std_newf_window(std);
|
||||
} break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// extension to the normal 'user' flags
|
||||
enum {
|
||||
MUI_STDF_FLAG_SAVEBOX = (1 << 8),
|
||||
};
|
||||
|
||||
mui_window_t *
|
||||
mui_stdfile_make_window(
|
||||
@ -535,7 +669,7 @@ mui_stdfile_make_window(
|
||||
}
|
||||
free(dup);
|
||||
}
|
||||
bool save_box = false;
|
||||
bool save_box = flags & MUI_STDF_FLAG_SAVEBOX;
|
||||
|
||||
mui_control_t * c = NULL;
|
||||
c2_rect_t cf;
|
||||
@ -566,12 +700,14 @@ mui_stdfile_make_window(
|
||||
cf, MUI_BUTTON_STYLE_NORMAL,
|
||||
"Home", MUI_STD_FILE_PART_HOME);
|
||||
c->key_equ = MUI_KEY_EQU(MUI_MODIFIER_ALT, 'h');
|
||||
mui_button_set_icon(c, MUI_ICON_HOME, 0);
|
||||
|
||||
c2_rect_top_of(&cf, cf.t, button_spacer);
|
||||
std->root = c = mui_button_new(w,
|
||||
cf, MUI_BUTTON_STYLE_NORMAL,
|
||||
"Root", MUI_STD_FILE_PART_ROOT);
|
||||
c->key_equ = MUI_KEY_EQU(MUI_MODIFIER_ALT, '/');
|
||||
mui_button_set_icon(c, MUI_ICON_ROOT, 0);
|
||||
|
||||
if (save_box) {
|
||||
c2_rect_top_of(&cf, cf.t, button_spacer);
|
||||
@ -580,6 +716,7 @@ mui_stdfile_make_window(
|
||||
"New…", MUI_STD_FILE_PART_ROOT);
|
||||
c->key_equ = MUI_KEY_EQU(MUI_MODIFIER_ALT, 'n');
|
||||
c->uid = MUI_STD_FILE_PART_NEW;
|
||||
mui_button_set_icon(c, MUI_ICON_FOLDER, 0);
|
||||
}
|
||||
cf = C2_RECT_WH(margin, 0, c2_rect_width(&wpos)-185, 35);
|
||||
c2_rect_top_of(&cf, c2_rect_height(&w->content), margin);
|
||||
@ -607,8 +744,8 @@ mui_stdfile_make_window(
|
||||
MUI_TEXT_ALIGN_RIGHT);
|
||||
// mui_control_set_state(c, MUI_CONTROL_STATE_DISABLED);
|
||||
// printf("Popup: %p\n", c);
|
||||
c = NULL;
|
||||
TAILQ_FOREACH(c, &w->controls, self) {
|
||||
c = mui_controls_first(&w->controls, MUI_CONTROLS_ALL);
|
||||
for (; c; c = mui_controls_next(c, MUI_CONTROLS_ALL)) {
|
||||
if (mui_control_get_uid(c))
|
||||
mui_control_set_action(c, _mui_stdfile_control_action, std);
|
||||
}
|
||||
@ -629,17 +766,35 @@ mui_stdfile_make_window(
|
||||
mui_window_t *
|
||||
mui_stdfile_get(
|
||||
struct mui_t * ui,
|
||||
c2_pt_t where,
|
||||
const char * prompt,
|
||||
const char * pattern,
|
||||
const char * start_path,
|
||||
c2_pt_t where, // pass 0,0 to center
|
||||
const char * prompt, // Window title
|
||||
const char * pattern, // Enforce any of these suffixes
|
||||
const char * start_path, // start in this path (optional)
|
||||
uint16_t flags )
|
||||
{
|
||||
mui_window_t *w = mui_stdfile_make_window(ui, where,
|
||||
prompt, pattern, start_path, NULL, flags);
|
||||
prompt, pattern, start_path, NULL,
|
||||
flags & ~MUI_STDF_FLAG_SAVEBOX);
|
||||
return w;
|
||||
}
|
||||
|
||||
mui_window_t *
|
||||
mui_stdfile_put(
|
||||
struct mui_t * ui,
|
||||
c2_pt_t where, // pass 0,0 to center
|
||||
const char * prompt, // Window title
|
||||
const char * pattern, // Enforce any of these suffixes
|
||||
const char * start_path, // start in this path (optional)
|
||||
const char * save_filename, // start with this filename
|
||||
uint16_t flags )
|
||||
{
|
||||
mui_window_t *w = mui_stdfile_make_window(ui, where,
|
||||
"Save As", pattern, start_path, save_filename,
|
||||
flags | MUI_STDF_FLAG_SAVEBOX);
|
||||
return w;
|
||||
}
|
||||
|
||||
|
||||
char *
|
||||
mui_stdfile_get_path(
|
||||
mui_window_t * w )
|
||||
|
@ -16,6 +16,7 @@ enum mui_window_part_e {
|
||||
MUI_WINDOW_PART_NONE = 0,
|
||||
MUI_WINDOW_PART_CONTENT,
|
||||
MUI_WINDOW_PART_TITLE,
|
||||
MUI_WINDOW_PART_CLOSEBOX,
|
||||
MUI_WINDOW_PART_FRAME,
|
||||
MUI_WINDOW_PART_COUNT,
|
||||
};
|
||||
@ -23,13 +24,41 @@ enum mui_window_part_e {
|
||||
static void
|
||||
mui_window_update_rects(
|
||||
mui_window_t *win,
|
||||
mui_font_t * main )
|
||||
mui_font_t * main,
|
||||
c2_rect_t parts[MUI_WINDOW_PART_COUNT] )
|
||||
{
|
||||
int title_height = main->size;
|
||||
c2_rect_t content = win->frame;
|
||||
c2_rect_inset(&content, 4, 4);
|
||||
content.t += title_height - 1;
|
||||
win->content = content;
|
||||
memset(parts, 0, sizeof(parts[0]) * MUI_WINDOW_PART_COUNT);
|
||||
parts[MUI_WINDOW_PART_CONTENT] = win->frame;
|
||||
c2_rect_inset(&parts[MUI_WINDOW_PART_CONTENT], 4, 4);
|
||||
parts[MUI_WINDOW_PART_CONTENT].t += title_height - 1;
|
||||
|
||||
stb_ttc_measure m = {};
|
||||
if (win->title) {
|
||||
mui_font_text_measure(main, win->title, &m);
|
||||
int title_width = m.x1 - m.x0;
|
||||
parts[MUI_WINDOW_PART_TITLE] = win->frame;
|
||||
parts[MUI_WINDOW_PART_TITLE].t += 1;
|
||||
parts[MUI_WINDOW_PART_TITLE].b =
|
||||
parts[MUI_WINDOW_PART_TITLE].t + title_height;
|
||||
parts[MUI_WINDOW_PART_TITLE].l +=
|
||||
-m.x0 + (c2_rect_width(&win->frame) / 2) - (title_width / 2);
|
||||
parts[MUI_WINDOW_PART_TITLE].r =
|
||||
parts[MUI_WINDOW_PART_TITLE].l + title_width;
|
||||
}
|
||||
if (win->flags.closebox) {
|
||||
mui_font_text_measure(main, MUI_GLYPH_CLOSEBOX, &m);
|
||||
parts[MUI_WINDOW_PART_CLOSEBOX] = win->frame;
|
||||
parts[MUI_WINDOW_PART_CLOSEBOX].t += 1;
|
||||
// TODO fix that fudge factor
|
||||
parts[MUI_WINDOW_PART_CLOSEBOX].b =
|
||||
parts[MUI_WINDOW_PART_CLOSEBOX].t + title_height - 4;
|
||||
parts[MUI_WINDOW_PART_CLOSEBOX].r =
|
||||
parts[MUI_WINDOW_PART_CLOSEBOX].l + (m.x1 - m.x0);
|
||||
c2_rect_offset(&parts[MUI_WINDOW_PART_CLOSEBOX],
|
||||
title_height / 2.5, 0);
|
||||
}
|
||||
win->content = parts[MUI_WINDOW_PART_CONTENT];
|
||||
}
|
||||
|
||||
void
|
||||
@ -41,7 +70,8 @@ mui_titled_window_draw(
|
||||
mui_font_t * main = mui_font_find(ui, "main");
|
||||
if (!main)
|
||||
return;
|
||||
mui_window_update_rects(win, main);
|
||||
c2_rect_t parts[MUI_WINDOW_PART_COUNT];
|
||||
mui_window_update_rects(win, main, parts);
|
||||
int title_height = main->size;
|
||||
|
||||
struct cg_ctx_t * cg = mui_drawable_get_cg(dr);
|
||||
@ -54,9 +84,11 @@ mui_titled_window_draw(
|
||||
|
||||
cg_set_line_width(cg, 1);
|
||||
cg_rectangle(cg, win->frame.l + 0.5f, win->frame.t + 0.5f,
|
||||
c2_rect_width(&win->frame) - 1, c2_rect_height(&win->frame) - 1);
|
||||
c2_rect_width(&win->frame) - 1,
|
||||
c2_rect_height(&win->frame) - 1);
|
||||
cg_rectangle(cg, win->content.l + 0.5f, win->content.t + 0.5f,
|
||||
c2_rect_width(&win->content) - 1, c2_rect_height(&win->content) - 1);
|
||||
c2_rect_width(&win->content) - 1,
|
||||
c2_rect_height(&win->content) - 1);
|
||||
cg_set_source_color(cg, &CG_COLOR(frameFill));
|
||||
cg_fill_preserve(cg);
|
||||
cg_set_source_color(cg, &CG_COLOR(frameColor));
|
||||
@ -74,18 +106,31 @@ mui_titled_window_draw(
|
||||
cg_set_source_color(cg, &CG_COLOR(decoColor));
|
||||
cg_stroke(cg);
|
||||
}
|
||||
if (win->title) {
|
||||
stb_ttc_measure m = {};
|
||||
mui_font_text_measure(main, win->title, &m);
|
||||
if (win->flags.closebox && isFront) {
|
||||
c2_rect_t close = parts[MUI_WINDOW_PART_CLOSEBOX];
|
||||
c2_rect_t out = close;
|
||||
c2_rect_inset(&out, -2, -0);
|
||||
cg_rectangle(cg, out.l, out.t,
|
||||
c2_rect_width(&out), c2_rect_height(&out));
|
||||
cg_set_source_color(cg, &CG_COLOR(frameFill));
|
||||
cg_fill(cg);
|
||||
|
||||
int title_width = m.x1 - m.x0;
|
||||
c2_rect_t title = win->frame;
|
||||
c2_rect_offset(&title, 0, 1);
|
||||
title.b = title.t + title_height;
|
||||
title.l += (c2_rect_width(&win->frame) / 2) - (title_width / 2);
|
||||
title.r = title.l + title_width;
|
||||
if (win->flags.in_part == MUI_WINDOW_PART_CLOSEBOX) {
|
||||
mui_font_text_draw(main, dr,
|
||||
C2_PT(close.l, close.t-2),
|
||||
MUI_GLYPH_CLICKBOX, strlen(MUI_GLYPH_CLICKBOX),
|
||||
isFront ? titleColor : dimTitleColor);
|
||||
} else {
|
||||
mui_font_text_draw(main, dr,
|
||||
C2_PT(close.l, close.t-2),
|
||||
MUI_GLYPH_CLOSEBOX, strlen(MUI_GLYPH_CLOSEBOX),
|
||||
isFront ? titleColor : decoColor);
|
||||
}
|
||||
}
|
||||
if (win->title) {
|
||||
c2_rect_t title = parts[MUI_WINDOW_PART_TITLE];
|
||||
if (isFront) {
|
||||
c2_rect_t titleBack = title;
|
||||
c2_rect_t titleBack = parts[MUI_WINDOW_PART_TITLE];
|
||||
c2_rect_inset(&titleBack, -6, 0);
|
||||
cg_round_rectangle(cg, titleBack.l, titleBack.t,
|
||||
c2_rect_width(&titleBack), c2_rect_height(&titleBack), 12, 12);
|
||||
@ -93,7 +138,7 @@ mui_titled_window_draw(
|
||||
cg_fill(cg);
|
||||
}
|
||||
mui_font_text_draw(main, dr,
|
||||
C2_PT(-m.x0 + 1 + title.l, title.t + 0),
|
||||
C2_PT(title.l, title.t),
|
||||
win->title, strlen(win->title),
|
||||
isFront ? titleColor : dimTitleColor);
|
||||
}
|
||||
@ -143,7 +188,7 @@ mui_window_create(
|
||||
struct mui_t *ui,
|
||||
c2_rect_t frame,
|
||||
mui_wdef_p wdef,
|
||||
uint8_t layer,
|
||||
uint32_t layer_flags,
|
||||
const char *title,
|
||||
uint32_t instance_size)
|
||||
{
|
||||
@ -154,15 +199,20 @@ mui_window_create(
|
||||
w->frame = frame;
|
||||
w->title = title ? strdup(title) : NULL;
|
||||
w->wdef = wdef ? wdef : mui_wdef_titlewindow;
|
||||
w->flags.layer = layer;
|
||||
w->flags.layer = layer_flags;
|
||||
if (layer_flags & MUI_WINDOW_FLAGS_CLOSEBOX)
|
||||
w->flags.closebox = true;
|
||||
mui_refqueue_init(&w->refs);
|
||||
TAILQ_INIT(&w->controls);
|
||||
mui_controls_init(&w->controls);
|
||||
mui_control_group_init(&w->main_group, &w->controls);
|
||||
STAILQ_INIT(&w->actions);
|
||||
pixman_region32_init(&w->inval);
|
||||
TAILQ_INSERT_HEAD(&ui->windows, w, self);
|
||||
mui_window_select(w); // place it in it's own layer
|
||||
mui_font_t * main = mui_font_find(ui, "main");
|
||||
mui_window_update_rects(w, main);
|
||||
// this is just to update content rects...
|
||||
c2_rect_t parts[MUI_WINDOW_PART_COUNT];
|
||||
mui_window_update_rects(w, main, parts);
|
||||
mui_window_inval(w, NULL); // just to mark the UI dirty
|
||||
|
||||
return w;
|
||||
@ -176,7 +226,7 @@ _mui_window_free(
|
||||
return;
|
||||
pixman_region32_fini(&win->inval);
|
||||
mui_control_t * c;
|
||||
while ((c = TAILQ_FIRST(&win->controls))) {
|
||||
while ((c = mui_controls_first(&win->controls, MUI_CONTROLS_ALL))) {
|
||||
mui_control_dispose(c);
|
||||
}
|
||||
if (win->title)
|
||||
@ -247,8 +297,10 @@ mui_window_draw(
|
||||
struct cg_ctx_t * cg = mui_drawable_get_cg(dr);
|
||||
cg_save(cg);
|
||||
// cg_translate(cg, content.l, content.t);
|
||||
mui_control_t * c, *safe;
|
||||
TAILQ_FOREACH_SAFE(c, &win->controls, self, safe) {
|
||||
mui_control_t * c = mui_controls_first(&win->controls, MUI_CONTROLS_VISIBLE),
|
||||
*safe = NULL;
|
||||
for (; c; c = safe) {
|
||||
safe = mui_controls_next(c, MUI_CONTROLS_VISIBLE);
|
||||
mui_control_draw(win, c, dr);
|
||||
}
|
||||
cg_restore(cg);
|
||||
@ -273,22 +325,23 @@ mui_window_handle_keyboard(
|
||||
// printf("%s %s handled it\n", __func__, win->title);
|
||||
return true;
|
||||
}
|
||||
// printf("%s %s checkint controls\n", __func__, win->title);
|
||||
// printf("%s %s checking controls\n", __func__, win->title);
|
||||
/*
|
||||
* Start with the control in focus, if there's any
|
||||
*/
|
||||
mui_control_t * first = win->control_focus.control ?
|
||||
win->control_focus.control :
|
||||
TAILQ_FIRST(&win->controls);
|
||||
mui_controls_first(&win->controls,
|
||||
MUI_CONTROLS_VISIBLE);
|
||||
mui_control_t * c = first;
|
||||
while (c) {
|
||||
if (mui_control_event(c, event)) {
|
||||
// printf("%s control %s handled it\n", __func__, c->title);
|
||||
return true;
|
||||
}
|
||||
c = TAILQ_NEXT(c, self);
|
||||
c = mui_controls_next(c, MUI_CONTROLS_VISIBLE);
|
||||
if (!c)
|
||||
c = TAILQ_FIRST(&win->controls);
|
||||
c = mui_controls_first(&win->controls, MUI_CONTROLS_VISIBLE);
|
||||
if (c == first)
|
||||
break;
|
||||
}
|
||||
@ -331,12 +384,23 @@ mui_window_handle_mouse(
|
||||
win->click_loc = event->mouse.where;
|
||||
c2_pt_offset(&win->click_loc, -win->frame.l, -win->frame.t);
|
||||
win->flags.hit_part = MUI_WINDOW_PART_FRAME;
|
||||
if (event->mouse.where.y < win->content.t)
|
||||
if (event->mouse.where.y < win->content.t) {
|
||||
win->flags.hit_part = MUI_WINDOW_PART_TITLE;
|
||||
else if (c2_rect_contains_pt(&win->content, &event->mouse.where))
|
||||
win->flags.hit_part = MUI_WINDOW_PART_CONTENT;
|
||||
// get the parts list now, apparently we need it
|
||||
mui_font_t * main = mui_font_find(win->ui, "main");
|
||||
c2_rect_t parts[MUI_WINDOW_PART_COUNT];
|
||||
mui_window_update_rects(win, main, parts);
|
||||
if (c2_rect_contains_pt(&parts[MUI_WINDOW_PART_CLOSEBOX],
|
||||
&event->mouse.where)) {
|
||||
win->flags.hit_part = win->flags.in_part =
|
||||
MUI_WINDOW_PART_CLOSEBOX;
|
||||
// redraw the titlebar
|
||||
mui_window_inval(win, NULL);
|
||||
}
|
||||
}
|
||||
} else
|
||||
win->flags.hit_part = MUI_WINDOW_PART_CONTENT;
|
||||
win->flags.hit_part = win->flags.in_part =
|
||||
MUI_WINDOW_PART_CONTENT;
|
||||
if (c) {
|
||||
if (c->cdef && c->cdef(c, MUI_CDEF_EVENT, event)) {
|
||||
// c->state = MUI_CONTROL_STATE_CLICKED;
|
||||
@ -347,27 +411,43 @@ mui_window_handle_mouse(
|
||||
return true;
|
||||
} break;
|
||||
case MUI_EVENT_DRAG:
|
||||
if (win->flags.hit_part == MUI_WINDOW_PART_TITLE) {
|
||||
if (win->flags.hit_part) {
|
||||
c2_rect_t frame = win->frame;
|
||||
c2_rect_offset(&frame,
|
||||
-win->frame.l + event->mouse.where.x - win->click_loc.x,
|
||||
-win->frame.t + event->mouse.where.y - win->click_loc.y);
|
||||
// todo, get that visibel rectangle from somewhere else
|
||||
c2_rect_t screen = { .br = win->ui->screen_size };
|
||||
screen.t += 35;
|
||||
c2_rect_t title_bar = frame;
|
||||
title_bar.b = title_bar.t + 35; // TODO fix that
|
||||
if (c2_rect_intersect_rect(&title_bar, &screen)) {
|
||||
c2_rect_t o;
|
||||
c2_rect_clip_rect(&title_bar, &screen, &o);
|
||||
if (c2_rect_width(&o) > 10 && c2_rect_height(&o) > 10) {
|
||||
mui_window_inval(win, NULL); // old frame
|
||||
win->frame = frame;
|
||||
mui_window_inval(win, NULL); // new frame
|
||||
if (win->flags.hit_part == MUI_WINDOW_PART_FRAME ||
|
||||
win->flags.hit_part == MUI_WINDOW_PART_TITLE) {
|
||||
// todo, get that visible rectangle from somewhere else
|
||||
c2_rect_t screen = { .br = win->ui->screen_size };
|
||||
screen.t += 35;
|
||||
c2_rect_t title_bar = frame;
|
||||
title_bar.b = title_bar.t + 35; // TODO fix that
|
||||
if (c2_rect_intersect_rect(&title_bar, &screen)) {
|
||||
c2_rect_t o;
|
||||
c2_rect_clip_rect(&title_bar, &screen, &o);
|
||||
if (c2_rect_width(&o) > 10 && c2_rect_height(&o) > 10) {
|
||||
mui_window_inval(win, NULL); // old frame
|
||||
win->frame = frame;
|
||||
mui_window_inval(win, NULL); // new frame
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else if (win->flags.hit_part == MUI_WINDOW_PART_CLOSEBOX) {
|
||||
mui_font_t * main = mui_font_find(win->ui, "main");
|
||||
c2_rect_t parts[MUI_WINDOW_PART_COUNT];
|
||||
mui_window_update_rects(win, main, parts);
|
||||
uint8_t inside = c2_rect_contains_pt(
|
||||
&parts[MUI_WINDOW_PART_CLOSEBOX],
|
||||
&event->mouse.where) ?
|
||||
MUI_WINDOW_PART_CLOSEBOX : MUI_WINDOW_PART_NONE;
|
||||
if (inside != win->flags.in_part) {
|
||||
win->flags.in_part = inside;
|
||||
mui_window_inval(win, NULL);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// mui_window_inval(win, NULL);
|
||||
return true;
|
||||
}
|
||||
if (win->control_clicked.control) {
|
||||
mui_control_t * c = win->control_clicked.control;
|
||||
@ -386,8 +466,16 @@ mui_window_handle_mouse(
|
||||
mui_control_t * c = win->control_clicked.control;
|
||||
mui_control_deref(&win->control_clicked);
|
||||
if (c->cdef && c->cdef(c, MUI_CDEF_EVENT, event))
|
||||
return true;
|
||||
part = MUI_WINDOW_PART_CONTENT;//return true;
|
||||
}
|
||||
if (win->flags.in_part == MUI_WINDOW_PART_CLOSEBOX) {
|
||||
mui_window_inval(win, NULL);
|
||||
bool close = true;
|
||||
mui_window_action(win, MUI_WINDOW_ACTION_CLOSEBOX, &close);
|
||||
if (close)
|
||||
mui_window_dispose(win);
|
||||
}
|
||||
win->flags.in_part = MUI_WINDOW_PART_NONE;
|
||||
return part != MUI_WINDOW_PART_NONE;
|
||||
} break;
|
||||
case MUI_EVENT_MOUSEENTER:
|
||||
|
@ -168,7 +168,7 @@ _init(
|
||||
FCC('c','p','u','m'),
|
||||
m_cpu_menu);
|
||||
// mii_mui_configure_slots(g->ui, &g_machine_conf);
|
||||
mii_mui_load_binary(g->ui, &g_loadbin_conf);
|
||||
// mii_mui_load_binary(g->ui, &g_loadbin_conf);
|
||||
// mii_mui_load_1mbrom(g->ui, &g_machine_conf.slot[0].conf.rom1mb);
|
||||
// mii_mui_load_2dsk(g->ui, &g_machine_conf.slot[0].conf.disk2, MII_2DSK_DISKII);
|
||||
// mii_mui_about(g->ui);
|
||||
@ -188,6 +188,14 @@ _init(
|
||||
"hdv,po,2mg",
|
||||
getenv("HOME"), 0);
|
||||
#endif
|
||||
#if 1
|
||||
mui_stdfile_put(ui,
|
||||
C2_PT(0, 0),
|
||||
"Save file as:",
|
||||
"woz,nib,dsk",
|
||||
getenv("HOME"),
|
||||
"My DiskImage.woz", 0);
|
||||
#endif
|
||||
|
||||
return g;
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ _test_show_about(
|
||||
}
|
||||
w = mui_alert(g->ui, C2_PT(0,0),
|
||||
"About MUI",
|
||||
"Version " MUI_VERSION "\n"
|
||||
"Version " "(dev)" "\n"
|
||||
"Build " __DATE__ " " __TIME__,
|
||||
MUI_ALERT_INFO);
|
||||
mui_window_set_id(w, FCC('a','b','o','t'));
|
||||
@ -88,7 +88,10 @@ _test_textedit_demo(
|
||||
c = mui_textedit_control_new(w, cf, MUI_CONTROL_TEXTBOX_FRAME);
|
||||
mui_textedit_set_text(c,
|
||||
"Fulling Mill Online Return Center.pdf");
|
||||
|
||||
mui_textedit_set_selection(c, 0, 255);
|
||||
uint glyph_start, glyph_end;
|
||||
mui_textedit_get_selection(c, &glyph_start, &glyph_end);
|
||||
printf("Selection %d:%d\n", glyph_start, glyph_end);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -206,7 +209,8 @@ _test_demo_all_controls(
|
||||
c2_rect_offset(&wpos,
|
||||
(ui->screen_size.x / 2) - (c2_rect_width(&wpos) / 2),
|
||||
(ui->screen_size.y * 0.45) - (c2_rect_height(&wpos) / 2));
|
||||
w = mui_window_create(ui, wpos, NULL, MUI_WINDOW_LAYER_NORMAL,
|
||||
w = mui_window_create(ui, wpos, NULL,
|
||||
MUI_WINDOW_LAYER_NORMAL + MUI_WINDOW_FLAGS_CLOSEBOX,
|
||||
"Control Demo", 0);
|
||||
mui_window_set_id(w, FCC('d','e','m','o'));
|
||||
|
||||
@ -554,9 +558,9 @@ _init(
|
||||
mui_menubar_add_simple(mbar, "Windows",
|
||||
FCC('w','i','n','d'),
|
||||
m_windows_menu);
|
||||
// _test_textedit_demo(ui);
|
||||
_test_textedit_demo(ui);
|
||||
// _test_static_text_and_boxes(ui);
|
||||
_test_demo_all_controls(ui);
|
||||
// _test_demo_all_controls(ui);
|
||||
return g;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user