From 150d7f3bd8b55751d827470101a9e9f8e556d0f7 Mon Sep 17 00:00:00 2001 From: Skyler Saleh Date: Fri, 21 May 2021 21:03:25 -0700 Subject: [PATCH] Initial skeleton Added initial skeleton of project. Including the window creation, build system, opcode parser, basic debugger and UI. --- .gitignore | 3 + CMakeLists.txt | 43 + LICENSE | 2 +- docs/gbops - The Game Boy opcode table.html | 1008 +++++ src/gui_textbox_extended.h | 1142 +++++ src/main.c | 492 ++ src/raygui.h | 4476 +++++++++++++++++++ src/ricons.h | 812 ++++ src/sb_instr_tables.h | 531 +++ tools/parse_gbops.py | 70 + 10 files changed, 8578 insertions(+), 1 deletion(-) create mode 100644 CMakeLists.txt create mode 100644 docs/gbops - The Game Boy opcode table.html create mode 100644 src/gui_textbox_extended.h create mode 100644 src/main.c create mode 100644 src/raygui.h create mode 100644 src/ricons.h create mode 100644 src/sb_instr_tables.h create mode 100755 tools/parse_gbops.py diff --git a/.gitignore b/.gitignore index 259148fa1..e9eab4dfa 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,6 @@ *.exe *.out *.app + +build* +.DS_Store diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 000000000..fb3b7d506 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,43 @@ +cmake_minimum_required(VERSION 3.11) # FetchContent is available in 3.11+ +project(SkyBoy) + +if (EMSCRIPTEN) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -s USE_GLFW=3 -s ASSERTIONS=1 -s WASM=1 -s ASYNCIFY") + set(CMAKE_EXECUTABLE_SUFFIX ".html") # This line is used to set your executable to build with the emscripten html template so taht you can directly open it. +endif () + +# Set this to the minimal version you want to support +find_package(raylib 3.0 QUIET) # Let CMake search for a raylib-config.cmake + +# You could change the QUIET above to REQUIRED and remove this if() clause +# This part downloads raylib and builds it if it's not installed on your system +if (NOT raylib_FOUND) # If there's none, fetch and build raylib + include(FetchContent) + + FetchContent_Declare( + raylib + URL https://github.com/raysan5/raylib/archive/master.tar.gz + ) + + FetchContent_GetProperties(raylib) + if (NOT raylib_POPULATED) # Have we downloaded raylib yet? + set(FETCHCONTENT_QUIET NO) + FetchContent_Populate(raylib) + + set(BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) # don't build the supplied examples + + # build raylib + add_subdirectory(${raylib_SOURCE_DIR} ${raylib_BINARY_DIR}) + + endif() + +endif() + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2") +# This is the main part: + +add_executable(${PROJECT_NAME} src/main.c) +#set(raylib_VERBOSE 1) +target_link_libraries(${PROJECT_NAME} raylib) + +# That's it! You should have an example executable that you can run. Have fun! diff --git a/LICENSE b/LICENSE index 1e9f1791d..17d822f57 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021 Sky +Copyright (c) 2021 Skyler "Sky" Saleh Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/docs/gbops - The Game Boy opcode table.html b/docs/gbops - The Game Boy opcode table.html new file mode 100644 index 000000000..6a1ddb502 --- /dev/null +++ b/docs/gbops - The Game Boy opcode table.html @@ -0,0 +1,1008 @@ + + +gbops - The Game Boy opcode table

gbops, the most accurate™ opcode table for the Game Boy

Help
Instruction format:
MNEMONIC ARGS
LENGTH DURATION
AFFECTED FLAGS

Example instruction:
XOR A,A
1 4t
Z​0​0​0

Flags?

The names for the flags that this opcode table uses are:
Z = "Zero", N = "Negative", H = "Half carry" and C = "Carry" (always in that order)
+ An instruction can do various things to the flags such as always set +(1), always reset (0), set or reset depending on the result of the +instrucion (letter for flag) or do nothing (-)

The search bar works by comparing properties of an opcode to a given number/string.
The currently supported properties are length|len, name and number|num|opcode|op|#.
The currently supported comparisions are <, <=, =, >= and >.
Numbers can be given in bases 16-10-8 and 2.

Some search examples:

.name="LD", this searches for all opcodes containing "LD" in their name.
.name="LD" .len=2, this searches for opcodes all containing "LD" in their name and a length of 2.
.name="LD" .len=2 or .opcode=0x76, same as the previous but it also shows all opcodes with index 0x76, which are HALT and BIT 6,(HL)

Help!!! Why isn't the search bar working!?

The search bar is currently a huge WIP and will have tons of bugs, if you find one please report it to The Issue Tracker

Glossary
Groups
x8/lsm
8-bit Load/Store/Move
x16/lsm
16-bit Load/Store/Move
x8/alu
8-bit Arithmetic Logic Unit
x16/alu
16-bit Arithmetic Logic Unit
x8/rsb
8-bit ???
control/br
branch
control/misc
misc
Units
T-Edge
A single tick of the Game Boy's clock, from low to high, or high to low - 8,388,608 hz
T-Cycle (t)
Two T-Edges - 4,194,304 hz
M-Cycle (m)
Four T-Cycles - 1,048,576 hz
h, 0x
Hexidecimal
Types
u8
8-bit unsigned integer
i8
8-bit signed integer
u16
16-bit unsigned integer
(...)
pointer dereference

The search bar is experimental, see help for syntax, it might fail unexpectedly.

If you find any bugs or something that seems out of place file an issue.
Unprefixed:
--+0+1+2+3+4+5+6+7+8+9+A+B+C+D+E+F
00+
NOP
+1 1m
+-​-​-​-
LD BC,u16
+3 3m
+-​-​-​-
LD (BC),A
+1 2m
+-​-​-​-
INC BC
+1 2m
+-​-​-​-
INC B
+1 1m
+Z​0​H​-
DEC B
+1 1m
+Z​1​H​-
LD B,u8
+2 2m
+-​-​-​-
RLCA
+1 1m
+0​0​0​C
LD (u16),SP
+3 5m
+-​-​-​-
ADD HL,BC
+1 2m
+-​0​H​C
LD A,(BC)
+1 2m
+-​-​-​-
DEC BC
+1 2m
+-​-​-​-
INC C
+1 1m
+Z​0​H​-
DEC C
+1 1m
+Z​1​H​-
LD C,u8
+2 2m
+-​-​-​-
RRCA
+1 1m
+0​0​0​C
10+
STOP
+2 1m
+-​-​-​-
LD DE,u16
+3 3m
+-​-​-​-
LD (DE),A
+1 2m
+-​-​-​-
INC DE
+1 2m
+-​-​-​-
INC D
+1 1m
+Z​0​H​-
DEC D
+1 1m
+Z​1​H​-
LD D,u8
+2 2m
+-​-​-​-
RLA
+1 1m
+0​0​0​C
JR i8
+2 3m
+-​-​-​-
ADD HL,DE
+1 2m
+-​0​H​C
LD A,(DE)
+1 2m
+-​-​-​-
DEC DE
+1 2m
+-​-​-​-
INC E
+1 1m
+Z​0​H​-
DEC E
+1 1m
+Z​1​H​-
LD E,u8
+2 2m
+-​-​-​-
RRA
+1 1m
+0​0​0​C
20+
JR NZ,i8
+2 2m-3m
+-​-​-​-
LD HL,u16
+3 3m
+-​-​-​-
LD (HL+),A
+1 2m
+-​-​-​-
INC HL
+1 2m
+-​-​-​-
INC H
+1 1m
+Z​0​H​-
DEC H
+1 1m
+Z​1​H​-
LD H,u8
+2 2m
+-​-​-​-
DAA
+1 1m
+Z​-​0​C
JR Z,i8
+2 2m-3m
+-​-​-​-
ADD HL,HL
+1 2m
+-​0​H​C
LD A,(HL+)
+1 2m
+-​-​-​-
DEC HL
+1 2m
+-​-​-​-
INC L
+1 1m
+Z​0​H​-
DEC L
+1 1m
+Z​1​H​-
LD L,u8
+2 2m
+-​-​-​-
CPL
+1 1m
+-​1​1​-
30+
JR NC,i8
+2 2m-3m
+-​-​-​-
LD SP,u16
+3 3m
+-​-​-​-
LD (HL-),A
+1 2m
+-​-​-​-
INC SP
+1 2m
+-​-​-​-
INC (HL)
+1 3m
+Z​0​H​-
DEC (HL)
+1 3m
+Z​1​H​-
LD (HL),u8
+2 3m
+-​-​-​-
SCF
+1 1m
+-​0​0​1
JR C,i8
+2 2m-3m
+-​-​-​-
ADD HL,SP
+1 2m
+-​0​H​C
LD A,(HL-)
+1 2m
+-​-​-​-
DEC SP
+1 2m
+-​-​-​-
INC A
+1 1m
+Z​0​H​-
DEC A
+1 1m
+Z​1​H​-
LD A,u8
+2 2m
+-​-​-​-
CCF
+1 1m
+-​0​0​C
40+
LD B,B
+1 1m
+-​-​-​-
LD B,C
+1 1m
+-​-​-​-
LD B,D
+1 1m
+-​-​-​-
LD B,E
+1 1m
+-​-​-​-
LD B,H
+1 1m
+-​-​-​-
LD B,L
+1 1m
+-​-​-​-
LD B,(HL)
+1 2m
+-​-​-​-
LD B,A
+1 1m
+-​-​-​-
LD C,B
+1 1m
+-​-​-​-
LD C,C
+1 1m
+-​-​-​-
LD C,D
+1 1m
+-​-​-​-
LD C,E
+1 1m
+-​-​-​-
LD C,H
+1 1m
+-​-​-​-
LD C,L
+1 1m
+-​-​-​-
LD C,(HL)
+1 2m
+-​-​-​-
LD C,A
+1 1m
+-​-​-​-
50+
LD D,B
+1 1m
+-​-​-​-
LD D,C
+1 1m
+-​-​-​-
LD D,D
+1 1m
+-​-​-​-
LD D,E
+1 1m
+-​-​-​-
LD D,H
+1 1m
+-​-​-​-
LD D,L
+1 1m
+-​-​-​-
LD D,(HL)
+1 2m
+-​-​-​-
LD D,A
+1 1m
+-​-​-​-
LD E,B
+1 1m
+-​-​-​-
LD E,C
+1 1m
+-​-​-​-
LD E,D
+1 1m
+-​-​-​-
LD E,E
+1 1m
+-​-​-​-
LD E,H
+1 1m
+-​-​-​-
LD E,L
+1 1m
+-​-​-​-
LD E,(HL)
+1 2m
+-​-​-​-
LD E,A
+1 1m
+-​-​-​-
60+
LD H,B
+1 1m
+-​-​-​-
LD H,C
+1 1m
+-​-​-​-
LD H,D
+1 1m
+-​-​-​-
LD H,E
+1 1m
+-​-​-​-
LD H,H
+1 1m
+-​-​-​-
LD H,L
+1 1m
+-​-​-​-
LD H,(HL)
+1 2m
+-​-​-​-
LD H,A
+1 1m
+-​-​-​-
LD L,B
+1 1m
+-​-​-​-
LD L,C
+1 1m
+-​-​-​-
LD L,D
+1 1m
+-​-​-​-
LD L,E
+1 1m
+-​-​-​-
LD L,H
+1 1m
+-​-​-​-
LD L,L
+1 1m
+-​-​-​-
LD L,(HL)
+1 2m
+-​-​-​-
LD L,A
+1 1m
+-​-​-​-
70+
LD (HL),B
+1 2m
+-​-​-​-
LD (HL),C
+1 2m
+-​-​-​-
LD (HL),D
+1 2m
+-​-​-​-
LD (HL),E
+1 2m
+-​-​-​-
LD (HL),H
+1 2m
+-​-​-​-
LD (HL),L
+1 2m
+-​-​-​-
HALT
+1 1m
+-​-​-​-
LD (HL),A
+1 2m
+-​-​-​-
LD A,B
+1 1m
+-​-​-​-
LD A,C
+1 1m
+-​-​-​-
LD A,D
+1 1m
+-​-​-​-
LD A,E
+1 1m
+-​-​-​-
LD A,H
+1 1m
+-​-​-​-
LD A,L
+1 1m
+-​-​-​-
LD A,(HL)
+1 2m
+-​-​-​-
LD A,A
+1 1m
+-​-​-​-
80+
ADD A,B
+1 1m
+Z​0​H​C
ADD A,C
+1 1m
+Z​0​H​C
ADD A,D
+1 1m
+Z​0​H​C
ADD A,E
+1 1m
+Z​0​H​C
ADD A,H
+1 1m
+Z​0​H​C
ADD A,L
+1 1m
+Z​0​H​C
ADD A,(HL)
+1 2m
+Z​0​H​C
ADD A,A
+1 1m
+Z​0​H​C
ADC A,B
+1 1m
+Z​0​H​C
ADC A,C
+1 1m
+Z​0​H​C
ADC A,D
+1 1m
+Z​0​H​C
ADC A,E
+1 1m
+Z​0​H​C
ADC A,H
+1 1m
+Z​0​H​C
ADC A,L
+1 1m
+Z​0​H​C
ADC A,(HL)
+1 2m
+Z​0​H​C
ADC A,A
+1 1m
+Z​0​H​C
90+
SUB A,B
+1 1m
+Z​1​H​C
SUB A,C
+1 1m
+Z​1​H​C
SUB A,D
+1 1m
+Z​1​H​C
SUB A,E
+1 1m
+Z​1​H​C
SUB A,H
+1 1m
+Z​1​H​C
SUB A,L
+1 1m
+Z​1​H​C
SUB A,(HL)
+1 2m
+Z​1​H​C
SUB A,A
+1 1m
+Z​1​H​C
SBC A,B
+1 1m
+Z​1​H​C
SBC A,C
+1 1m
+Z​1​H​C
SBC A,D
+1 1m
+Z​1​H​C
SBC A,E
+1 1m
+Z​1​H​C
SBC A,H
+1 1m
+Z​1​H​C
SBC A,L
+1 1m
+Z​1​H​C
SBC A,(HL)
+1 2m
+Z​1​H​C
SBC A,A
+1 1m
+Z​1​H​C
A0+
AND A,B
+1 1m
+Z​0​1​0
AND A,C
+1 1m
+Z​0​1​0
AND A,D
+1 1m
+Z​0​1​0
AND A,E
+1 1m
+Z​0​1​0
AND A,H
+1 1m
+Z​0​1​0
AND A,L
+1 1m
+Z​0​1​0
AND A,(HL)
+1 2m
+Z​0​1​0
AND A,A
+1 1m
+Z​0​1​0
XOR A,B
+1 1m
+Z​0​0​0
XOR A,C
+1 1m
+Z​0​0​0
XOR A,D
+1 1m
+Z​0​0​0
XOR A,E
+1 1m
+Z​0​0​0
XOR A,H
+1 1m
+Z​0​0​0
XOR A,L
+1 1m
+Z​0​0​0
XOR A,(HL)
+1 2m
+Z​0​0​0
XOR A,A
+1 1m
+Z​0​0​0
B0+
OR A,B
+1 1m
+Z​0​0​0
OR A,C
+1 1m
+Z​0​0​0
OR A,D
+1 1m
+Z​0​0​0
OR A,E
+1 1m
+Z​0​0​0
OR A,H
+1 1m
+Z​0​0​0
OR A,L
+1 1m
+Z​0​0​0
OR A,(HL)
+1 2m
+Z​0​0​0
OR A,A
+1 1m
+Z​0​0​0
CP A,B
+1 1m
+Z​1​H​C
CP A,C
+1 1m
+Z​1​H​C
CP A,D
+1 1m
+Z​1​H​C
CP A,E
+1 1m
+Z​1​H​C
CP A,H
+1 1m
+Z​1​H​C
CP A,L
+1 1m
+Z​1​H​C
CP A,(HL)
+1 2m
+Z​1​H​C
CP A,A
+1 1m
+Z​1​H​C
C0+
RET NZ
+1 2m-5m
+-​-​-​-
POP BC
+1 3m
+-​-​-​-
JP NZ,u16
+3 3m-4m
+-​-​-​-
JP u16
+3 4m
+-​-​-​-
CALL NZ,u16
+3 3m-6m
+-​-​-​-
PUSH BC
+1 4m
+-​-​-​-
ADD A,u8
+2 2m
+Z​0​H​C
RST 00h
+1 4m
+-​-​-​-
RET Z
+1 2m-5m
+-​-​-​-
RET
+1 4m
+-​-​-​-
JP Z,u16
+3 3m-4m
+-​-​-​-
PREFIX CB
+1 1m
+-​-​-​-
CALL Z,u16
+3 3m-6m
+-​-​-​-
CALL u16
+3 6m
+-​-​-​-
ADC A,u8
+2 2m
+Z​0​H​C
RST 08h
+1 4m
+-​-​-​-
D0+
RET NC
+1 2m-5m
+-​-​-​-
POP DE
+1 3m
+-​-​-​-
JP NC,u16
+3 3m-4m
+-​-​-​-
CALL NC,u16
+3 3m-6m
+-​-​-​-
PUSH DE
+1 4m
+-​-​-​-
SUB A,u8
+2 2m
+Z​1​H​C
RST 10h
+1 4m
+-​-​-​-
RET C
+1 2m-5m
+-​-​-​-
RETI
+1 4m
+-​-​-​-
JP C,u16
+3 3m-4m
+-​-​-​-
CALL C,u16
+3 3m-6m
+-​-​-​-
SBC A,u8
+2 2m
+Z​1​H​C
RST 18h
+1 4m
+-​-​-​-
E0+
LD (FF00+u8),A
+2 3m
+-​-​-​-
POP HL
+1 3m
+-​-​-​-
LD (FF00+C),A
+1 2m
+-​-​-​-
PUSH HL
+1 4m
+-​-​-​-
AND A,u8
+2 2m
+Z​0​1​0
RST 20h
+1 4m
+-​-​-​-
ADD SP,i8
+2 4m
+0​0​H​C
JP HL
+1 1m
+-​-​-​-
LD (u16),A
+3 4m
+-​-​-​-
XOR A,u8
+2 2m
+Z​0​0​0
RST 28h
+1 4m
+-​-​-​-
F0+
LD A,(FF00+u8)
+2 3m
+-​-​-​-
POP AF
+1 3m
+Z​N​H​C
LD A,(FF00+C)
+1 2m
+-​-​-​-
DI
+1 1m
+-​-​-​-
PUSH AF
+1 4m
+-​-​-​-
OR A,u8
+2 2m
+Z​0​0​0
RST 30h
+1 4m
+-​-​-​-
LD HL,SP+i8
+2 3m
+0​0​H​C
LD SP,HL
+1 2m
+-​-​-​-
LD A,(u16)
+3 4m
+-​-​-​-
EI
+1 1m
+-​-​-​-
CP A,u8
+2 2m
+Z​1​H​C
RST 38h
+1 4m
+-​-​-​-
0xCB Prefixed:
--+0+1+2+3+4+5+6+7+8+9+A+B+C+D+E+F
00+
RLC B
+2 2m
+Z​0​0​C
RLC C
+2 2m
+Z​0​0​C
RLC D
+2 2m
+Z​0​0​C
RLC E
+2 2m
+Z​0​0​C
RLC H
+2 2m
+Z​0​0​C
RLC L
+2 2m
+Z​0​0​C
RLC (HL)
+2 4m
+Z​0​0​C
RLC A
+2 2m
+Z​0​0​C
RRC B
+2 2m
+Z​0​0​C
RRC C
+2 2m
+Z​0​0​C
RRC D
+2 2m
+Z​0​0​C
RRC E
+2 2m
+Z​0​0​C
RRC H
+2 2m
+Z​0​0​C
RRC L
+2 2m
+Z​0​0​C
RRC (HL)
+2 4m
+Z​0​0​C
RRC A
+2 2m
+Z​0​0​C
10+
RL B
+2 2m
+Z​0​0​C
RL C
+2 2m
+Z​0​0​C
RL D
+2 2m
+Z​0​0​C
RL E
+2 2m
+Z​0​0​C
RL H
+2 2m
+Z​0​0​C
RL L
+2 2m
+Z​0​0​C
RL (HL)
+2 4m
+Z​0​0​C
RL A
+2 2m
+Z​0​0​C
RR B
+2 2m
+Z​0​0​C
RR C
+2 2m
+Z​0​0​C
RR D
+2 2m
+Z​0​0​C
RR E
+2 2m
+Z​0​0​C
RR H
+2 2m
+Z​0​0​C
RR L
+2 2m
+Z​0​0​C
RR (HL)
+2 4m
+Z​0​0​C
RR A
+2 2m
+Z​0​0​C
20+
SLA B
+2 2m
+Z​0​0​C
SLA C
+2 2m
+Z​0​0​C
SLA D
+2 2m
+Z​0​0​C
SLA E
+2 2m
+Z​0​0​C
SLA H
+2 2m
+Z​0​0​C
SLA L
+2 2m
+Z​0​0​C
SLA (HL)
+2 4m
+Z​0​0​C
SLA A
+2 2m
+Z​0​0​C
SRA B
+2 2m
+Z​0​0​C
SRA C
+2 2m
+Z​0​0​C
SRA D
+2 2m
+Z​0​0​C
SRA E
+2 2m
+Z​0​0​C
SRA H
+2 2m
+Z​0​0​C
SRA L
+2 2m
+Z​0​0​C
SRA (HL)
+2 4m
+Z​0​0​C
SRA A
+2 2m
+Z​0​0​C
30+
SWAP B
+2 2m
+Z​0​0​0
SWAP C
+2 2m
+Z​0​0​0
SWAP D
+2 2m
+Z​0​0​0
SWAP E
+2 2m
+Z​0​0​0
SWAP H
+2 2m
+Z​0​0​0
SWAP L
+2 2m
+Z​0​0​0
SWAP (HL)
+2 4m
+Z​0​0​0
SWAP A
+2 2m
+Z​0​0​0
SRL B
+2 2m
+Z​0​0​C
SRL C
+2 2m
+Z​0​0​C
SRL D
+2 2m
+Z​0​0​C
SRL E
+2 2m
+Z​0​0​C
SRL H
+2 2m
+Z​0​0​C
SRL L
+2 2m
+Z​0​0​C
SRL (HL)
+2 4m
+Z​0​0​C
SRL A
+2 2m
+Z​0​0​C
40+
BIT 0,B
+2 2m
+Z​0​1​-
BIT 0,C
+2 2m
+Z​0​1​-
BIT 0,D
+2 2m
+Z​0​1​-
BIT 0,E
+2 2m
+Z​0​1​-
BIT 0,H
+2 2m
+Z​0​1​-
BIT 0,L
+2 2m
+Z​0​1​-
BIT 0,(HL)
+2 3m
+Z​0​1​-
BIT 0,A
+2 2m
+Z​0​1​-
BIT 1,B
+2 2m
+Z​0​1​-
BIT 1,C
+2 2m
+Z​0​1​-
BIT 1,D
+2 2m
+Z​0​1​-
BIT 1,E
+2 2m
+Z​0​1​-
BIT 1,H
+2 2m
+Z​0​1​-
BIT 1,L
+2 2m
+Z​0​1​-
BIT 1,(HL)
+2 3m
+Z​0​1​-
BIT 1,A
+2 2m
+Z​0​1​-
50+
BIT 2,B
+2 2m
+Z​0​1​-
BIT 2,C
+2 2m
+Z​0​1​-
BIT 2,D
+2 2m
+Z​0​1​-
BIT 2,E
+2 2m
+Z​0​1​-
BIT 2,H
+2 2m
+Z​0​1​-
BIT 2,L
+2 2m
+Z​0​1​-
BIT 2,(HL)
+2 3m
+Z​0​1​-
BIT 2,A
+2 2m
+Z​0​1​-
BIT 3,B
+2 2m
+Z​0​1​-
BIT 3,C
+2 2m
+Z​0​1​-
BIT 3,D
+2 2m
+Z​0​1​-
BIT 3,E
+2 2m
+Z​0​1​-
BIT 3,H
+2 2m
+Z​0​1​-
BIT 3,L
+2 2m
+Z​0​1​-
BIT 3,(HL)
+2 3m
+Z​0​1​-
BIT 3,A
+2 2m
+Z​0​1​-
60+
BIT 4,B
+2 2m
+Z​0​1​-
BIT 4,C
+2 2m
+Z​0​1​-
BIT 4,D
+2 2m
+Z​0​1​-
BIT 4,E
+2 2m
+Z​0​1​-
BIT 4,H
+2 2m
+Z​0​1​-
BIT 4,L
+2 2m
+Z​0​1​-
BIT 4,(HL)
+2 3m
+Z​0​1​-
BIT 4,A
+2 2m
+Z​0​1​-
BIT 5,B
+2 2m
+Z​0​1​-
BIT 5,C
+2 2m
+Z​0​1​-
BIT 5,D
+2 2m
+Z​0​1​-
BIT 5,E
+2 2m
+Z​0​1​-
BIT 5,H
+2 2m
+Z​0​1​-
BIT 5,L
+2 2m
+Z​0​1​-
BIT 5,(HL)
+2 3m
+Z​0​1​-
BIT 5,A
+2 2m
+Z​0​1​-
70+
BIT 6,B
+2 2m
+Z​0​1​-
BIT 6,C
+2 2m
+Z​0​1​-
BIT 6,D
+2 2m
+Z​0​1​-
BIT 6,E
+2 2m
+Z​0​1​-
BIT 6,H
+2 2m
+Z​0​1​-
BIT 6,L
+2 2m
+Z​0​1​-
BIT 6,(HL)
+2 3m
+Z​0​1​-
BIT 6,A
+2 2m
+Z​0​1​-
BIT 7,B
+2 2m
+Z​0​1​-
BIT 7,C
+2 2m
+Z​0​1​-
BIT 7,D
+2 2m
+Z​0​1​-
BIT 7,E
+2 2m
+Z​0​1​-
BIT 7,H
+2 2m
+Z​0​1​-
BIT 7,L
+2 2m
+Z​0​1​-
BIT 7,(HL)
+2 3m
+Z​0​1​-
BIT 7,A
+2 2m
+Z​0​1​-
80+
RES 0,B
+2 2m
+-​-​-​-
RES 0,C
+2 2m
+-​-​-​-
RES 0,D
+2 2m
+-​-​-​-
RES 0,E
+2 2m
+-​-​-​-
RES 0,H
+2 2m
+-​-​-​-
RES 0,L
+2 2m
+-​-​-​-
RES 0,(HL)
+2 4m
+-​-​-​-
RES 0,A
+2 2m
+-​-​-​-
RES 1,B
+2 2m
+-​-​-​-
RES 1,C
+2 2m
+-​-​-​-
RES 1,D
+2 2m
+-​-​-​-
RES 1,E
+2 2m
+-​-​-​-
RES 1,H
+2 2m
+-​-​-​-
RES 1,L
+2 2m
+-​-​-​-
RES 1,(HL)
+2 4m
+-​-​-​-
RES 1,A
+2 2m
+-​-​-​-
90+
RES 2,B
+2 2m
+-​-​-​-
RES 2,C
+2 2m
+-​-​-​-
RES 2,D
+2 2m
+-​-​-​-
RES 2,E
+2 2m
+-​-​-​-
RES 2,H
+2 2m
+-​-​-​-
RES 2,L
+2 2m
+-​-​-​-
RES 2,(HL)
+2 4m
+-​-​-​-
RES 2,A
+2 2m
+-​-​-​-
RES 3,B
+2 2m
+-​-​-​-
RES 3,C
+2 2m
+-​-​-​-
RES 3,D
+2 2m
+-​-​-​-
RES 3,E
+2 2m
+-​-​-​-
RES 3,H
+2 2m
+-​-​-​-
RES 3,L
+2 2m
+-​-​-​-
RES 3,(HL)
+2 4m
+-​-​-​-
RES 3,A
+2 2m
+-​-​-​-
A0+
RES 4,B
+2 2m
+-​-​-​-
RES 4,C
+2 2m
+-​-​-​-
RES 4,D
+2 2m
+-​-​-​-
RES 4,E
+2 2m
+-​-​-​-
RES 4,H
+2 2m
+-​-​-​-
RES 4,L
+2 2m
+-​-​-​-
RES 4,(HL)
+2 4m
+-​-​-​-
RES 4,A
+2 2m
+-​-​-​-
RES 5,B
+2 2m
+-​-​-​-
RES 5,C
+2 2m
+-​-​-​-
RES 5,D
+2 2m
+-​-​-​-
RES 5,E
+2 2m
+-​-​-​-
RES 5,H
+2 2m
+-​-​-​-
RES 5,L
+2 2m
+-​-​-​-
RES 5,(HL)
+2 4m
+-​-​-​-
RES 5,A
+2 2m
+-​-​-​-
B0+
RES 6,B
+2 2m
+-​-​-​-
RES 6,C
+2 2m
+-​-​-​-
RES 6,D
+2 2m
+-​-​-​-
RES 6,E
+2 2m
+-​-​-​-
RES 6,H
+2 2m
+-​-​-​-
RES 6,L
+2 2m
+-​-​-​-
RES 6,(HL)
+2 4m
+-​-​-​-
RES 6,A
+2 2m
+-​-​-​-
RES 7,B
+2 2m
+-​-​-​-
RES 7,C
+2 2m
+-​-​-​-
RES 7,D
+2 2m
+-​-​-​-
RES 7,E
+2 2m
+-​-​-​-
RES 7,H
+2 2m
+-​-​-​-
RES 7,L
+2 2m
+-​-​-​-
RES 7,(HL)
+2 4m
+-​-​-​-
RES 7,A
+2 2m
+-​-​-​-
C0+
SET 0,B
+2 2m
+-​-​-​-
SET 0,C
+2 2m
+-​-​-​-
SET 0,D
+2 2m
+-​-​-​-
SET 0,E
+2 2m
+-​-​-​-
SET 0,H
+2 2m
+-​-​-​-
SET 0,L
+2 2m
+-​-​-​-
SET 0,(HL)
+2 4m
+-​-​-​-
SET 0,A
+2 2m
+-​-​-​-
SET 1,B
+2 2m
+-​-​-​-
SET 1,C
+2 2m
+-​-​-​-
SET 1,D
+2 2m
+-​-​-​-
SET 1,E
+2 2m
+-​-​-​-
SET 1,H
+2 2m
+-​-​-​-
SET 1,L
+2 2m
+-​-​-​-
SET 1,(HL)
+2 4m
+-​-​-​-
SET 1,A
+2 2m
+-​-​-​-
D0+
SET 2,B
+2 2m
+-​-​-​-
SET 2,C
+2 2m
+-​-​-​-
SET 2,D
+2 2m
+-​-​-​-
SET 2,E
+2 2m
+-​-​-​-
SET 2,H
+2 2m
+-​-​-​-
SET 2,L
+2 2m
+-​-​-​-
SET 2,(HL)
+2 4m
+-​-​-​-
SET 2,A
+2 2m
+-​-​-​-
SET 3,B
+2 2m
+-​-​-​-
SET 3,C
+2 2m
+-​-​-​-
SET 3,D
+2 2m
+-​-​-​-
SET 3,E
+2 2m
+-​-​-​-
SET 3,H
+2 2m
+-​-​-​-
SET 3,L
+2 2m
+-​-​-​-
SET 3,(HL)
+2 4m
+-​-​-​-
SET 3,A
+2 2m
+-​-​-​-
E0+
SET 4,B
+2 2m
+-​-​-​-
SET 4,C
+2 2m
+-​-​-​-
SET 4,D
+2 2m
+-​-​-​-
SET 4,E
+2 2m
+-​-​-​-
SET 4,H
+2 2m
+-​-​-​-
SET 4,L
+2 2m
+-​-​-​-
SET 4,(HL)
+2 4m
+-​-​-​-
SET 4,A
+2 2m
+-​-​-​-
SET 5,B
+2 2m
+-​-​-​-
SET 5,C
+2 2m
+-​-​-​-
SET 5,D
+2 2m
+-​-​-​-
SET 5,E
+2 2m
+-​-​-​-
SET 5,H
+2 2m
+-​-​-​-
SET 5,L
+2 2m
+-​-​-​-
SET 5,(HL)
+2 4m
+-​-​-​-
SET 5,A
+2 2m
+-​-​-​-
F0+
SET 6,B
+2 2m
+-​-​-​-
SET 6,C
+2 2m
+-​-​-​-
SET 6,D
+2 2m
+-​-​-​-
SET 6,E
+2 2m
+-​-​-​-
SET 6,H
+2 2m
+-​-​-​-
SET 6,L
+2 2m
+-​-​-​-
SET 6,(HL)
+2 4m
+-​-​-​-
SET 6,A
+2 2m
+-​-​-​-
SET 7,B
+2 2m
+-​-​-​-
SET 7,C
+2 2m
+-​-​-​-
SET 7,D
+2 2m
+-​-​-​-
SET 7,E
+2 2m
+-​-​-​-
SET 7,H
+2 2m
+-​-​-​-
SET 7,L
+2 2m
+-​-​-​-
SET 7,(HL)
+2 4m
+-​-​-​-
SET 7,A
+2 2m
+-​-​-​-
\ No newline at end of file diff --git a/src/gui_textbox_extended.h b/src/gui_textbox_extended.h new file mode 100644 index 000000000..019c2566c --- /dev/null +++ b/src/gui_textbox_extended.h @@ -0,0 +1,1142 @@ +/******************************************************************************************* + * + * Text box extended (cursor positioning and editing) + * + * MODULE USAGE: + * #define GUI_TEXTBOX_EXTENDED_IMPLEMENTATION + * #include "gui_textbox_extended.h" + * + * On game draw call: GuiTextBoxEx(...); + * + * LICENSE: zlib/libpng + * + * Copyright (c) 2019 Vlad Adrian (@Demizdor) and Ramon Santamaria (@raysan5) + * + * This software is provided "as-is", without any express or implied warranty. + *In no event will the authors be held liable for any damages arising from the + *use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + *including commercial applications, and to alter it and redistribute it freely, + *subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + *claim that you wrote the original software. If you use this software in a + *product, an acknowledgment in the product documentation would be appreciated + *but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + *be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + *distribution. + * + **********************************************************************************************/ + +#ifndef GUI_TEXTBOX_EXTENDED_H +#define GUI_TEXTBOX_EXTENDED_H + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +//... + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- + +// Text box state data +typedef struct GuiTextBoxState { + int cursor; // Cursor position in text + int start; // Text start position (from where we begin drawing the text) + int index; // Text start index (index inside the text of `start` always in + // sync) + int select; // Marks position of cursor when selection has started +} GuiTextBoxState; + +#ifdef __cplusplus +extern "C" { // Prevents name mangling of functions +#endif + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- +RAYGUIDEF void GuiTextBoxSetActive(Rectangle bounds); // Sets the active textbox +RAYGUIDEF Rectangle GuiTextBoxGetActive(void); // Get bounds of active textbox + +RAYGUIDEF void +GuiTextBoxSetCursor(int cursor); // Set cursor position of active textbox +RAYGUIDEF int +GuiTextBoxGetCursor(void); // Get cursor position of active textbox + +RAYGUIDEF void +GuiTextBoxSetSelection(int start, + int length); // Set selection of active textbox +RAYGUIDEF Vector2 +GuiTextBoxGetSelection(void); // Get selection of active textbox (x - selection + // start y - selection length) + +RAYGUIDEF bool GuiTextBoxIsActive( + Rectangle bounds); // Returns true if a textbox control with specified + // `bounds` is the active textbox +RAYGUIDEF GuiTextBoxState +GuiTextBoxGetState(void); // Get state for the active textbox +RAYGUIDEF void GuiTextBoxSetState( + GuiTextBoxState state); // Set state for the active textbox (state must be + // valid else things will break) + +RAYGUIDEF void GuiTextBoxSelectAll( + const char *text); // Select all characters in the active textbox (same as + // pressing `CTRL` + `A`) +RAYGUIDEF void GuiTextBoxCopy( + const char *text); // Copy selected text to clipboard from the active + // textbox (same as pressing `CTRL` + `C`) +RAYGUIDEF void +GuiTextBoxPaste(char *text, + int textSize); // Paste text from clipboard into the textbox + // (same as pressing `CTRL` + `V`) +RAYGUIDEF void +GuiTextBoxCut(char *text); // Cut selected text in the active textbox and copy + // it to clipboard (same as pressing `CTRL` + `X`) +RAYGUIDEF int GuiTextBoxDelete( + char *text, int length, + bool before); // Deletes a character or selection before from the active + // textbox (depending on `before`). Returns bytes deleted. +RAYGUIDEF int GuiTextBoxGetByteIndex( + const char *text, int start, int from, + int to); // Get the byte index for a character starting at position `from` + // with index `start` until position `to`. + +RAYGUIDEF bool GuiTextBoxEx(Rectangle bounds, char *text, int textSize, + bool editMode); + +#ifdef __cplusplus +} +#endif + +#endif // GUI_TEXTBOX_EXTENDED_H + +/*********************************************************************************** + * + * GUI TEXTBOX EXTENDED IMPLEMENTATION + * + ************************************************************************************/ + +#if defined(GUI_TEXTBOX_EXTENDED_IMPLEMENTATION) + +#ifndef RAYGUI_H +#include "raygui.h" +#endif + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +//... + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +// Cursor measure mode +typedef enum { + GUI_MEASURE_MODE_CURSOR_END = 0xA, + GUI_MEASURE_MODE_CURSOR_POS, + GUI_MEASURE_MODE_CURSOR_COORDS +} GuiMeasureMode; + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +static Rectangle guiTextBoxActive = {0}; // Area of the currently active textbox + +static GuiTextBoxState guiTextBoxState = { // Keeps state of the active textbox + .cursor = -1, + .start = 0, + .index = 0, + .select = -1}; + +//---------------------------------------------------------------------------------- +// Module Internal Functions Declaration +//---------------------------------------------------------------------------------- +static int GetPrevCodepoint(const char *text, const char *start, int *prev); +static int GuiMeasureTextBox(const char *text, int length, Rectangle rec, + int *pos, int mode); +static int GuiMeasureTextBoxRev( + const char *text, int length, Rectangle rec, + int *pos); // Highly synchronized with calculations in DrawTextRecEx() + +static inline int GuiTextBoxGetCursorCoordinates( + const char *text, int length, Rectangle rec, + int pos); // Calculate cursor coordinates based on the cursor position `pos` + // inside the `text`. +static inline int +GuiTextBoxGetCursorFromMouse(const char *text, int length, Rectangle rec, + int *pos); // Calculate cursor position in textbox + // based on mouse coordinates. +static inline int +GuiTextBoxMaxCharacters(const char *text, int length, + Rectangle rec); // Calculates how many characters is the + // textbox able to draw inside rec +static inline unsigned int GuiCountCodepointsUntilNewline( + const char *text); // Returns total number of characters(codepoints) in a + // UTF8 encoded `text` until `\0` or a `\n` is found. + +static inline void MoveTextBoxCursorRight(const char *text, int length, + Rectangle textRec); +static inline void MoveTextBoxCursorLeft(const char *text); + +static int EncodeCodepoint(unsigned int c, char out[5]); + +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- + +// Sets the active textbox (reseting state of the previous active textbox) +RAYGUIDEF void GuiTextBoxSetActive(Rectangle bounds) { + guiTextBoxActive = bounds; + guiTextBoxState = + (GuiTextBoxState){.cursor = -1, .start = 0, .index = 0, .select = -1}; +} + +// Gets bounds of active textbox +RAYGUIDEF Rectangle GuiTextBoxGetActive(void) { return guiTextBoxActive; } + +// Set cursor position of active textbox +RAYGUIDEF void GuiTextBoxSetCursor(int cursor) { + guiTextBoxState.cursor = (cursor < 0) ? -1 : cursor; + guiTextBoxState.start = -1; // Mark this to be recalculated +} + +// Get cursor position of active textbox +RAYGUIDEF int GuiTextBoxGetCursor(void) { return guiTextBoxState.cursor; } + +// Set selection of active textbox +RAYGUIDEF void GuiTextBoxSetSelection(int start, int length) { + if (start < 0) + start = 0; + if (length < 0) + length = 0; + + GuiTextBoxSetCursor(start + length); + guiTextBoxState.select = start; +} + +// Get selection of active textbox +RAYGUIDEF Vector2 GuiTextBoxGetSelection(void) { + if (guiTextBoxState.select == -1 || + guiTextBoxState.select == guiTextBoxState.cursor) + return RAYGUI_CLITERAL(Vector2){0}; + else if (guiTextBoxState.cursor > guiTextBoxState.select) + return RAYGUI_CLITERAL(Vector2){guiTextBoxState.select, + guiTextBoxState.cursor - + guiTextBoxState.select}; + + return RAYGUI_CLITERAL(Vector2){ + guiTextBoxState.cursor, guiTextBoxState.select - guiTextBoxState.cursor}; +} + +// Returns true if a textbox control with specified `bounds` is the active +// textbox +RAYGUIDEF bool GuiTextBoxIsActive(Rectangle bounds) { + return (bounds.x == guiTextBoxActive.x && bounds.y == guiTextBoxActive.y && + bounds.width == guiTextBoxActive.width && + bounds.height == guiTextBoxActive.height); +} + +RAYGUIDEF GuiTextBoxState GuiTextBoxGetState(void) { return guiTextBoxState; } +RAYGUIDEF void GuiTextBoxSetState(GuiTextBoxState state) { + // NOTE: should we check if state values are valid ?!? + guiTextBoxState = state; +} + +RAYGUIDEF int GuiTextBoxGetByteIndex(const char *text, int start, int from, + int to) { + int i = start, k = from; + + while ((text[i] != '\0') && (k < to)) { + int j = 0; + int letter = GetNextCodepoint(&text[i], &j); + + if (letter == 0x3f) + j = 1; + i += j; + ++k; + } + + return i; +} + +RAYGUIDEF int GuiTextBoxDelete(char *text, int length, bool before) { + if ((guiTextBoxState.cursor != -1) && (text != NULL)) { + int startIdx = 0, endIdx = 0; + if ((guiTextBoxState.select != -1) && + (guiTextBoxState.select != guiTextBoxState.cursor)) { + // Delete selection + int start = guiTextBoxState.cursor; + int end = guiTextBoxState.select; + + if (guiTextBoxState.cursor > guiTextBoxState.select) { + start = guiTextBoxState.select; + end = guiTextBoxState.cursor; + } + + // Convert to byte indexes + startIdx = GuiTextBoxGetByteIndex(text, 0, 0, start); + endIdx = GuiTextBoxGetByteIndex(text, 0, 0, end); + + // Adjust text box state + guiTextBoxState.cursor = start; // Always set cursor to start of selection + if (guiTextBoxState.select < guiTextBoxState.start) + guiTextBoxState.start = -1; // Force to recalculate on the next frame + } else { + if (before) { + // Delete character before cursor + if (guiTextBoxState.cursor != 0) { + endIdx = GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.cursor); + guiTextBoxState.cursor--; + startIdx = GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.cursor); + + if (guiTextBoxState.cursor < guiTextBoxState.start) + guiTextBoxState.start = + -1; // Force to recalculate on the next frame + } + } else { + // Delete character after cursor + if (guiTextBoxState.cursor + 1 <= + GuiCountCodepointsUntilNewline(text)) { + startIdx = GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.cursor); + endIdx = + GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.cursor + 1); + } + } + } + + memmove(&text[startIdx], &text[endIdx], length - endIdx); + text[length - (endIdx - startIdx)] = '\0'; + guiTextBoxState.select = -1; // Always deselect + + return (endIdx - startIdx); + } + + return 0; +} + +RAYGUIDEF void GuiTextBoxSelectAll(const char *text) { + guiTextBoxState.cursor = GuiCountCodepointsUntilNewline(text); + + if (guiTextBoxState.cursor > 0) { + guiTextBoxState.select = 0; + guiTextBoxState.start = -1; // Force recalculate on the next frame + } else + guiTextBoxState.select = -1; +} + +RAYGUIDEF void GuiTextBoxCopy(const char *text) { + if ((text != NULL) && (guiTextBoxState.select != -1) && + (guiTextBoxState.cursor != -1) && + (guiTextBoxState.select != guiTextBoxState.cursor)) { + int start = guiTextBoxState.cursor; + int end = guiTextBoxState.select; + + if (guiTextBoxState.cursor > guiTextBoxState.select) { + start = guiTextBoxState.select; + end = guiTextBoxState.cursor; + } + + // Convert to byte indexes + start = GuiTextBoxGetByteIndex(text, 0, 0, start); + end = GuiTextBoxGetByteIndex(text, 0, 0, end); + + // FIXME: `TextSubtext()` only lets use copy TEXTSPLIT_MAX_TEXT_LENGTH + // (1024) bytes maybe modify `SetClipboardText()` so we can use it only on + // part of a string + const char *clipText = TextSubtext(text, start, end - start); + + SetClipboardText(clipText); + } +} + +// Paste text from clipboard into the active textbox. +// `text` is the pointer to the buffer used by the textbox while `textSize` is +// the text buffer max size +RAYGUIDEF void GuiTextBoxPaste(char *text, int textSize) { + const char *clipText = + GetClipboardText(); // GLFW guaratees this should be UTF8 encoded! + int length = strlen(text); + + if ((text != NULL) && (clipText != NULL) && (guiTextBoxState.cursor != -1)) { + if ((guiTextBoxState.select != -1) && + (guiTextBoxState.select != guiTextBoxState.cursor)) { + // If there's a selection we'll have to delete it first + length -= GuiTextBoxDelete(text, length, true); + } + + int clipLen = strlen(clipText); // We want the length in bytes + + // Calculate how many bytes can we copy from clipboard text before we run + // out of space + int size = ((length + clipLen) <= textSize) ? clipLen : textSize - length; + + // Make room by shifting to right the bytes after cursor + int startIdx = GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.cursor); + int endIdx = startIdx + size; + memmove(&text[endIdx], &text[startIdx], length - startIdx); + text[length + size] = '\0'; // Set the NULL char + + // At long last copy the clipboard text + memcpy(&text[startIdx], clipText, size); + + // Set cursor position at the end of the pasted text + guiTextBoxState.cursor = 0; + + for (int i = 0; i < (startIdx + size); guiTextBoxState.cursor++) { + int next = 0; + int letter = GetNextCodepoint(&text[i], &next); + if (letter != 0x3f) + i += next; + else + i += 1; + } + + guiTextBoxState.start = -1; // Force to recalculate on the next frame + } +} + +RAYGUIDEF void GuiTextBoxCut(char *text) { + if ((text != NULL) && (guiTextBoxState.select != -1) && + (guiTextBoxState.cursor != -1) && + (guiTextBoxState.select != guiTextBoxState.cursor)) { + // First copy selection to clipboard; + int start = guiTextBoxState.cursor, end = guiTextBoxState.select; + + if (guiTextBoxState.cursor > guiTextBoxState.select) { + start = guiTextBoxState.select; + end = guiTextBoxState.cursor; + } + + // Convert to byte indexes + int startIdx = GuiTextBoxGetByteIndex(text, 0, 0, start); + int endIdx = GuiTextBoxGetByteIndex(text, 0, 0, end); + + // FIXME: `TextSubtext()` only lets use copy TEXTSPLIT_MAX_TEXT_LENGTH + // (1024) bytes maybe modify `SetClipboardText()` so we can use it only on + // parts of a string + const char *clipText = TextSubtext(text, startIdx, endIdx - startIdx); + SetClipboardText(clipText); + + // Now delete selection (copy data over it) + int len = strlen(text); + memmove(&text[startIdx], &text[endIdx], len - endIdx); + text[len - (endIdx - startIdx)] = '\0'; + + // Adjust text box state + guiTextBoxState.cursor = start; // Always set cursor to start of selection + if (guiTextBoxState.select < guiTextBoxState.start) + guiTextBoxState.start = -1; // Force to recalculate + guiTextBoxState.select = -1; // Deselect + } +} + +// A text box control supporting text selection, cursor positioning and commonly +// used keyboard shortcuts. NOTE 1: Requires static variables: framesCounter +// NOTE 2: Returns if KEY_ENTER pressed (useful for data validation) +RAYGUIDEF bool GuiTextBoxEx(Rectangle bounds, char *text, int textSize, + bool editMode) { +// Define the cursor movement/selection speed when movement keys are +// held/pressed +#define TEXTBOX_CURSOR_COOLDOWN 5 + + static int framesCounter = 0; // Required for blinking cursor + + GuiControlState state = guiState; + bool pressed = false; + + // Make sure length doesn't exceed `textSize`. `textSize` is actually the max + // amount of characters the textbox can handle. + int length = strlen(text); + if (length > textSize) { + text[textSize] = '\0'; + length = textSize; + } + + // Make sure we have enough room to draw at least 1 character + if ((bounds.width - 2 * GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING)) < + GuiGetStyle(DEFAULT, TEXT_SIZE)) { + bounds.width = GuiGetStyle(DEFAULT, TEXT_SIZE) + + 2 * GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING); + } + + // Center the text vertically + int verticalPadding = + (bounds.height - 2 * GuiGetStyle(TEXTBOX, BORDER_WIDTH) - + GuiGetStyle(DEFAULT, TEXT_SIZE)) / + 2; + + if (verticalPadding < 0) { + // Make sure the height is sufficient + bounds.height = 2 * GuiGetStyle(TEXTBOX, BORDER_WIDTH) + + GuiGetStyle(DEFAULT, TEXT_SIZE); + verticalPadding = 0; + } + + // Calculate the drawing area for the text inside the control `bounds` + Rectangle textRec = { + bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH) + + GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING), + bounds.y + verticalPadding + GuiGetStyle(TEXTBOX, BORDER_WIDTH), + bounds.width - 2 * (GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING) + + GuiGetStyle(TEXTBOX, BORDER_WIDTH)), + GuiGetStyle(DEFAULT, TEXT_SIZE)}; + + Vector2 cursorPos = { + textRec.x, + textRec.y}; // This holds the coordinates inside textRec of the cursor at + // current position and will be recalculated later + bool active = GuiTextBoxIsActive( + bounds); // Check if this textbox is the global active textbox + + int selStart = 0, selLength = 0, textStartIndex = 0; + + // Update control + //-------------------------------------------------------------------- + if ((state != GUI_STATE_DISABLED) && !guiLocked) { + Vector2 mousePoint = GetMousePosition(); + + if (editMode) { + // Check if we are the global active textbox + // A textbox becomes active when the user clicks it :) + if (!active) { + if (CheckCollisionPointRec(mousePoint, bounds) && + (IsMouseButtonPressed(MOUSE_LEFT_BUTTON) || + IsMouseButtonPressed(MOUSE_RIGHT_BUTTON))) { + // Hurray!!! we just became the active textbox + active = true; + GuiTextBoxSetActive(bounds); + } + } else if (!CheckCollisionPointRec(mousePoint, bounds) && + IsMouseButtonPressed(MOUSE_RIGHT_BUTTON)) { + // When active and the right mouse is clicked outside the textbox we + // should deactivate it NOTE: We set a dummy rect as the active textbox + // bounds + GuiTextBoxSetActive(RAYGUI_CLITERAL(Rectangle){0, 0, -1, -1}); + active = false; + } + + if (active) { + state = GUI_STATE_PRESSED; + framesCounter++; + + // Make sure state doesn't have invalid values + if (guiTextBoxState.cursor > length) + guiTextBoxState.cursor = -1; + if (guiTextBoxState.select > length) + guiTextBoxState.select = -1; + if (guiTextBoxState.start > length) + guiTextBoxState.start = -1; + + // Check textbox state for changes and recalculate if necesary + if (guiTextBoxState.cursor == -1) { + // Set cursor to last visible character in textbox + guiTextBoxState.cursor = + GuiTextBoxMaxCharacters(text, length, textRec); + } + + if (guiTextBoxState.start == -1) { + // Force recalculate text start position and text start index + + // NOTE: start and index are always in sync + // start will hold the starting character position from where the text + // will be drawn while index will hold the byte index inside the text + // for that character + + if (guiTextBoxState.cursor == 0) { + guiTextBoxState.start = guiTextBoxState.index = + 0; // No need to recalculate + } else { + int pos = 0; + int len = + GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.cursor); + guiTextBoxState.index = + GuiMeasureTextBoxRev(text, len, textRec, &pos); + guiTextBoxState.start = guiTextBoxState.cursor - pos + 1; + } + } + + // ----------------- + // HANDLE KEY INPUT + // ----------------- + // * -> | LSHIFT + -> move cursor to the right | increase selection by + // one + // * <- | LSHIFT + <- move cursor to the left | decrease selection by + // one + // * HOME | LSHIFT + HOME moves cursor to start of text | selects text + // from cursor to start of text + // * END | LSHIFT + END move cursor to end of text | selects text from + // cursor until end of text + // * CTRL + A select all characters in text + // * CTRL + C copy selected text + // * CTRL + X cut selected text + // * CTRL + V remove selected text, if any, then paste clipboard data + // * DEL delete character or selection after cursor + // * BACKSPACE delete character or selection before cursor + // TODO: Add more shortcuts (insert mode, select word, moveto/select + // prev/next word ...) + if (IsKeyPressed(KEY_RIGHT) || + (IsKeyDown(KEY_RIGHT) && + (framesCounter % TEXTBOX_CURSOR_COOLDOWN == 0))) { + if (IsKeyDown(KEY_LEFT_SHIFT)) { + // Selecting + if (guiTextBoxState.select == -1) + guiTextBoxState.select = + guiTextBoxState.cursor; // Mark selection start + + MoveTextBoxCursorRight(text, length, textRec); + } else { + if (guiTextBoxState.select != -1 && + guiTextBoxState.select != guiTextBoxState.cursor) { + // Deselect and move cursor to end of selection + if (guiTextBoxState.cursor < guiTextBoxState.select) { + guiTextBoxState.cursor = guiTextBoxState.select - 1; + MoveTextBoxCursorRight(text, length, textRec); + } + } else { + // Move cursor to the right + MoveTextBoxCursorRight(text, length, textRec); + } + + guiTextBoxState.select = -1; + } + + framesCounter = 0; + } else if (IsKeyPressed(KEY_LEFT) || + (IsKeyDown(KEY_LEFT) && + (framesCounter % TEXTBOX_CURSOR_COOLDOWN == 0))) { + if (IsKeyDown(KEY_LEFT_SHIFT)) { + // Selecting + if (guiTextBoxState.select == -1) + guiTextBoxState.select = + guiTextBoxState.cursor; // Mark selection start + + MoveTextBoxCursorLeft(text); + } else { + if ((guiTextBoxState.select != -1) && + (guiTextBoxState.select != guiTextBoxState.cursor)) { + // Deselect and move cursor to start of selection + if (guiTextBoxState.cursor > guiTextBoxState.select) { + guiTextBoxState.cursor = guiTextBoxState.select; + + if (guiTextBoxState.start > guiTextBoxState.cursor) { + guiTextBoxState.start = guiTextBoxState.cursor; + guiTextBoxState.index = GuiTextBoxGetByteIndex( + text, 0, 0, + guiTextBoxState.start); // Recalculate byte index + } + } + } else { + // Move cursor to the left + MoveTextBoxCursorLeft(text); + } + + guiTextBoxState.select = -1; + } + + framesCounter = 0; + } else if (IsKeyPressed(KEY_BACKSPACE) || + (IsKeyDown(KEY_BACKSPACE) && + (framesCounter % TEXTBOX_CURSOR_COOLDOWN) == 0)) { + GuiTextBoxDelete(text, length, true); + } else if (IsKeyPressed(KEY_DELETE) || + (IsKeyDown(KEY_DELETE) && + (framesCounter % TEXTBOX_CURSOR_COOLDOWN) == 0)) { + GuiTextBoxDelete(text, length, false); + } else if (IsKeyPressed(KEY_HOME)) { + if (IsKeyDown(KEY_LEFT_SHIFT)) { + // Select from start of text to cursor + if ((guiTextBoxState.select > guiTextBoxState.cursor) || + ((guiTextBoxState.select == -1) && + (guiTextBoxState.cursor != 0))) { + guiTextBoxState.select = guiTextBoxState.cursor; + } + } else + guiTextBoxState.select = -1; // Deselect everything + + // Move cursor to start of text + guiTextBoxState.cursor = guiTextBoxState.start = + guiTextBoxState.index = 0; + framesCounter = 0; + } else if (IsKeyPressed(KEY_END)) { + int max = GuiCountCodepointsUntilNewline(text); + + if (IsKeyDown(KEY_LEFT_SHIFT)) { + if ((guiTextBoxState.select == -1) && + (guiTextBoxState.cursor != max)) { + guiTextBoxState.select = guiTextBoxState.cursor; + } + } else + guiTextBoxState.select = -1; // Deselect everything + + int pos = 0; + guiTextBoxState.cursor = max; + int len = GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.cursor); + guiTextBoxState.index = + GuiMeasureTextBoxRev(text, len, textRec, &pos); + guiTextBoxState.start = guiTextBoxState.cursor - pos + 1; + } else if (IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_A)) + GuiTextBoxSelectAll(text); // CTRL + A > Select all + else if (IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_C)) + GuiTextBoxCopy(text); // CTRL + C > Copy selected text to clipboard + else if (IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_X)) + GuiTextBoxCut(text); // CTRL + X > Cut selected text + else if (IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_V)) + GuiTextBoxPaste(text, textSize); // CTRL + V > Paste clipboard text + else if (IsKeyPressed(KEY_ENTER)) + pressed = true; + else { + int key = GetKeyPressed(); + if ((key >= 32) && ((guiTextBoxState.cursor + 1) < textSize)) { + if ((guiTextBoxState.select != -1) && + (guiTextBoxState.select != guiTextBoxState.cursor)) { + // Delete selection + GuiTextBoxDelete(text, length, true); + } + + // Decode codepoint + char out[5] = {0}; + int sz = EncodeCodepoint(key, &out[0]); + + if (sz != 0) { + int startIdx = + GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.cursor); + int endIdx = startIdx + sz; + + if (endIdx <= textSize && length < textSize - 1) { + guiTextBoxState.cursor++; + guiTextBoxState.select = -1; + memmove(&text[endIdx], &text[startIdx], length - startIdx); + memcpy(&text[startIdx], &out[0], sz); + length += sz; + text[length] = '\0'; + + if (guiTextBoxState.start != -1) { + const int max = GuiTextBoxMaxCharacters( + &text[guiTextBoxState.index], + length - guiTextBoxState.index, textRec); + + if ((guiTextBoxState.cursor - guiTextBoxState.start) > max) + guiTextBoxState.start = -1; + } + } + } + } + } + + // ------------- + // HANDLE MOUSE + // ------------- + if (CheckCollisionPointRec(mousePoint, bounds)) { + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { + if (CheckCollisionPointRec(mousePoint, textRec)) { + GuiTextBoxGetCursorFromMouse(&text[guiTextBoxState.index], + length - guiTextBoxState.index, + textRec, &guiTextBoxState.cursor); + guiTextBoxState.cursor += guiTextBoxState.start; + guiTextBoxState.select = -1; + } else { + // Clicked outside the `textRec` but still inside bounds + if (mousePoint.x <= bounds.x + bounds.width / 2) + guiTextBoxState.cursor = 0 + guiTextBoxState.start; + else + guiTextBoxState.cursor = + guiTextBoxState.start + + GuiTextBoxMaxCharacters(&text[guiTextBoxState.index], + length - guiTextBoxState.index, + textRec); + guiTextBoxState.select = -1; + } + } else if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) { + int cursor = guiTextBoxState.cursor - guiTextBoxState.start; + bool move = false; + if (CheckCollisionPointRec(mousePoint, textRec)) { + GuiTextBoxGetCursorFromMouse(&text[guiTextBoxState.index], + length - guiTextBoxState.index, + textRec, &cursor); + } else { + // Clicked outside the `textRec` but still inside bounds, this + // means that we must move the text + move = true; + if (mousePoint.x > bounds.x + bounds.width / 2) { + cursor = GuiTextBoxMaxCharacters(&text[guiTextBoxState.index], + length - guiTextBoxState.index, + textRec); + } + } + + guiTextBoxState.cursor = cursor + guiTextBoxState.start; + + if (guiTextBoxState.select == -1) { + // Mark start of selection + guiTextBoxState.select = guiTextBoxState.cursor; + } + + // Move the text when cursor is positioned before or after the text + if ((framesCounter % TEXTBOX_CURSOR_COOLDOWN) == 0 && move) { + if (cursor == 0) + MoveTextBoxCursorLeft(text); + else if (cursor == GuiTextBoxMaxCharacters( + &text[guiTextBoxState.index], + length - guiTextBoxState.index, textRec)) { + MoveTextBoxCursorRight(text, length, textRec); + } + } + } + } + + // Calculate X coordinate of the blinking cursor + cursorPos.x = GuiTextBoxGetCursorCoordinates( + &text[guiTextBoxState.index], length - guiTextBoxState.index, + textRec, guiTextBoxState.cursor - guiTextBoxState.start); + + // Update variables + textStartIndex = guiTextBoxState.index; + + if (guiTextBoxState.select == -1) { + selStart = guiTextBoxState.cursor; + selLength = 0; + } else if (guiTextBoxState.cursor > guiTextBoxState.select) { + selStart = guiTextBoxState.select; + selLength = guiTextBoxState.cursor - guiTextBoxState.select; + } else { + selStart = guiTextBoxState.cursor; + selLength = guiTextBoxState.select - guiTextBoxState.cursor; + } + + // We aren't drawing all of the text so make sure `DrawTextRecEx()` is + // selecting things correctly + if (guiTextBoxState.start > selStart) { + selLength -= guiTextBoxState.start - selStart; + selStart = 0; + } else + selStart = selStart - guiTextBoxState.start; + } else + state = GUI_STATE_FOCUSED; + + if (IsKeyPressed(KEY_ENTER) || + (!CheckCollisionPointRec(mousePoint, bounds) && + IsMouseButtonPressed(0))) + pressed = true; + } else { + if (active && IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_C)) { + // If active copy all text to clipboard even when disabled + + // Backup textbox state + int select = guiTextBoxState.select; + int cursor = guiTextBoxState.cursor; + int start = guiTextBoxState.start; + + if ((guiTextBoxState.select == -1) || + (guiTextBoxState.select == guiTextBoxState.cursor)) { + // If no selection then mark all text to be copied to clipboard + GuiTextBoxSelectAll(text); + } + + GuiTextBoxCopy(text); + + // Restore textbox state + guiTextBoxState.select = select; + guiTextBoxState.cursor = cursor; + guiTextBoxState.start = start; + } + + if (CheckCollisionPointRec(mousePoint, bounds)) { + state = GUI_STATE_FOCUSED; + if (IsMouseButtonPressed(0)) + pressed = true; + } + } + + if (pressed) + framesCounter = 0; + } + + // Draw control + //-------------------------------------------------------------------- + DrawRectangleLinesEx( + bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), + Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state * 3))), guiAlpha)); + + if (state == GUI_STATE_PRESSED) { + DrawRectangle( + bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH), + bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH), + bounds.width - 2 * GuiGetStyle(TEXTBOX, BORDER_WIDTH), + bounds.height - 2 * GuiGetStyle(TEXTBOX, BORDER_WIDTH), + Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_FOCUSED)), guiAlpha)); + + // Draw blinking cursor + if (editMode && active && + ((framesCounter / TEXTEDIT_CURSOR_BLINK_FRAMES) % 2 == 0) && + selLength == 0) { + DrawRectangle( + cursorPos.x, cursorPos.y, 1, GuiGetStyle(DEFAULT, TEXT_SIZE) * 2, + Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED)), guiAlpha)); + } + } else if (state == GUI_STATE_DISABLED) { + DrawRectangle( + bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH), + bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH), + bounds.width - 2 * GuiGetStyle(TEXTBOX, BORDER_WIDTH), + bounds.height - 2 * GuiGetStyle(TEXTBOX, BORDER_WIDTH), + Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED)), guiAlpha)); + } + + // Finally draw the text and selection + DrawTextRecEx( + guiFont, &text[textStartIndex], textRec, GuiGetStyle(DEFAULT, TEXT_SIZE), + GuiGetStyle(DEFAULT, TEXT_SPACING), false, + Fade(GetColor(GuiGetStyle(TEXTBOX, TEXT + (state * 3))), guiAlpha), + selStart, selLength, GetColor(GuiGetStyle(TEXTBOX, COLOR_SELECTED_FG)), + GetColor(GuiGetStyle(TEXTBOX, COLOR_SELECTED_BG))); + + return pressed; +} + +//---------------------------------------------------------------------------------- +// Module Internal Functions Definition +//---------------------------------------------------------------------------------- + +static int GetPrevCodepoint(const char *text, const char *start, int *prev) { + int c = 0x3f; + char *p = (char *)text; + *prev = 1; + + for (int i = 0; (p >= start) && (i < 4); p--, i++) { + if ((((unsigned char)*p) >> 6) != 2) { + c = GetNextCodepoint(p, prev); + break; + } + } + + return c; +} + +// Returns total number of characters(codepoints) in a UTF8 encoded `text` until +// `\0` or a `\n` is found. NOTE: If a invalid UTF8 sequence is encountered a +// `?`(0x3f) codepoint is counted instead. +static inline unsigned int GuiCountCodepointsUntilNewline(const char *text) { + unsigned int len = 0; + char *ptr = (char *)&text[0]; + + while ((*ptr != '\0') && (*ptr != '\n')) { + int next = 0; + int letter = GetNextCodepoint(ptr, &next); + + if (letter == 0x3f) + ptr += 1; + else + ptr += next; + ++len; + } + + return len; +} + +// Highly synchronized with calculations in DrawTextRecEx() +static int GuiMeasureTextBox(const char *text, int length, Rectangle rec, + int *pos, int mode) { + // Get gui font properties + const Font font = guiFont; + const float fontSize = GuiGetStyle(DEFAULT, TEXT_SIZE); + const float spacing = GuiGetStyle(DEFAULT, TEXT_SPACING); + + int textOffsetX = 0; // Offset between characters + float scaleFactor = 0.0f; + + int letter = 0; // Current character + int index = 0; // Index position in sprite font + + scaleFactor = fontSize / font.baseSize; + + int i = 0, k = 0; + int glyphWidth = 0; + + for (i = 0; i < length; i++, k++) { + glyphWidth = 0; + int next = 1; + letter = GetNextCodepoint(&text[i], &next); + if (letter == 0x3f) + next = 1; + index = GetGlyphIndex(font, letter); + i += next - 1; + + if (letter != '\n') { + glyphWidth = + (font.chars[index].advanceX == 0) + ? (int)(font.recs[index].width * scaleFactor + spacing) + : (int)(font.chars[index].advanceX * scaleFactor + spacing); + + if ((textOffsetX + glyphWidth + 1) >= rec.width) + break; + + if ((mode == GUI_MEASURE_MODE_CURSOR_POS) && (*pos == k)) + break; + else if (mode == GUI_MEASURE_MODE_CURSOR_COORDS) { + // Check if the mouse pointer is inside the glyph rect + Rectangle grec = {rec.x + textOffsetX - 1, rec.y, glyphWidth, + (font.baseSize + font.baseSize / 2) * scaleFactor - + 1}; + Vector2 mouse = GetMousePosition(); + + if (CheckCollisionPointRec(mouse, grec)) { + // Smooth selection by dividing the glyph rectangle into 2 equal parts + // and checking where the mouse resides + if (mouse.x > (grec.x + glyphWidth / 2)) { + textOffsetX += glyphWidth; + k++; + } + + break; + } + } + } else + break; + + textOffsetX += glyphWidth; + } + + *pos = k; + + return (rec.x + textOffsetX - 1); +} + +// Required by GuiTextBoxEx() +// Highly synchronized with calculations in DrawTextRecEx() +static int GuiMeasureTextBoxRev(const char *text, int length, Rectangle rec, + int *pos) { + // Get gui font properties + const Font font = guiFont; + const float fontSize = GuiGetStyle(DEFAULT, TEXT_SIZE); + const float spacing = GuiGetStyle(DEFAULT, TEXT_SPACING); + + int textOffsetX = 0; // Offset between characters + float scaleFactor = 0.0f; + + int letter = 0; // Current character + int index = 0; // Index position in sprite font + + scaleFactor = fontSize / font.baseSize; + + int i = 0, k = 0; + int glyphWidth = 0, prev = 1; + for (i = length; i >= 0; i--, k++) { + glyphWidth = 0; + letter = GetPrevCodepoint(&text[i], &text[0], &prev); + + if (letter == 0x3f) + prev = 1; + index = GetGlyphIndex(font, letter); + i -= prev - 1; + + if (letter != '\n') { + glyphWidth = + (font.chars[index].advanceX == 0) + ? (int)(font.recs[index].width * scaleFactor + spacing) + : (int)(font.chars[index].advanceX * scaleFactor + spacing); + + if ((textOffsetX + glyphWidth + 1) >= rec.width) + break; + } else + break; + + textOffsetX += glyphWidth; + } + + *pos = k; + + return (i + prev); +} + +// Calculate cursor coordinates based on the cursor position `pos` inside the +// `text`. +static inline int GuiTextBoxGetCursorCoordinates(const char *text, int length, + Rectangle rec, int pos) { + return GuiMeasureTextBox(text, length, rec, &pos, + GUI_MEASURE_MODE_CURSOR_POS); +} + +// Calculate cursor position in textbox based on mouse coordinates. +static inline int GuiTextBoxGetCursorFromMouse(const char *text, int length, + Rectangle rec, int *pos) { + return GuiMeasureTextBox(text, length, rec, pos, + GUI_MEASURE_MODE_CURSOR_COORDS); +} + +// Calculates how many characters is the textbox able to draw inside rec +static inline int GuiTextBoxMaxCharacters(const char *text, int length, + Rectangle rec) { + int pos = -1; + GuiMeasureTextBox(text, length, rec, &pos, GUI_MEASURE_MODE_CURSOR_END); + return pos; +} + +static inline void MoveTextBoxCursorRight(const char *text, int length, + Rectangle textRec) { + // FIXME: Counting codepoints each time we press the key is expensive, find + // another way + int count = GuiCountCodepointsUntilNewline(text); + if (guiTextBoxState.cursor < count) + guiTextBoxState.cursor++; + + const int max = GuiTextBoxMaxCharacters( + &text[guiTextBoxState.index], length - guiTextBoxState.index, textRec); + + if ((guiTextBoxState.cursor - guiTextBoxState.start) > max) { + const int cidx = + GuiTextBoxGetByteIndex(text, guiTextBoxState.index, + guiTextBoxState.start, guiTextBoxState.cursor); + int pos = 0; + guiTextBoxState.index = GuiMeasureTextBoxRev(text, cidx - 1, textRec, &pos); + guiTextBoxState.start = guiTextBoxState.cursor - pos; + } +} + +static inline void MoveTextBoxCursorLeft(const char *text) { + if (guiTextBoxState.cursor > 0) + guiTextBoxState.cursor--; + + if (guiTextBoxState.cursor < guiTextBoxState.start) { + int prev = 0; + int letter = + GetPrevCodepoint(&text[guiTextBoxState.index - 1], text, &prev); + if (letter == 0x3f) + prev = 1; + guiTextBoxState.start--; + guiTextBoxState.index -= prev; + } +} + +static int EncodeCodepoint(unsigned int c, char out[5]) { + int len = 0; + if (c <= 0x7f) { + out[0] = (char)c; + len = 1; + } else if (c <= 0x7ff) { + out[0] = (char)(((c >> 6) & 0x1f) | 0xc0); + out[1] = (char)((c & 0x3f) | 0x80); + len = 2; + } else if (c <= 0xffff) { + out[0] = (char)(((c >> 12) & 0x0f) | 0xe0); + out[1] = (char)(((c >> 6) & 0x3f) | 0x80); + out[2] = (char)((c & 0x3f) | 0x80); + len = 3; + } else if (c <= 0x10ffff) { + out[0] = (char)(((c >> 18) & 0x07) | 0xf0); + out[1] = (char)(((c >> 12) & 0x3f) | 0x80); + out[2] = (char)(((c >> 6) & 0x3f) | 0x80); + out[3] = (char)((c & 0x3f) | 0x80); + len = 4; + } + + out[len] = 0; + return len; +} + +#endif // GUI_TEXTBOX_EXTENDED_IMPLEMENTATION diff --git a/src/main.c b/src/main.c new file mode 100644 index 000000000..b8a9c3278 --- /dev/null +++ b/src/main.c @@ -0,0 +1,492 @@ +/***************************************************************************** + * + * SkyBoy GB Emulator + * + * Copyright (c) 2021 Skyler "Sky" Saleh + * + **/ + +typedef void *sb_opcode_impl_t; +#include "raylib.h" +#include "sb_instr_tables.h" +#include +#define RAYGUI_IMPLEMENTATION +#define RAYGUI_SUPPORT_ICONS +#include "raygui.h" +#if defined(PLATFORM_WEB) +#include +#endif +Vector2 ballPosition = {200 / 2.0f, 200 / 2.0f}; +Vector2 ballSpeed = {5.0f, 4.0f}; +float ballRadius = 20; + +int framesCounter = 0; + +const int GUI_PADDING = 10; +const int GUI_ROW_HEIGHT = 30; +const int GUI_LABEL_HEIGHT = 0; +const int GUI_LABEL_PADDING = 5; +#define MAX_CARTRIDGE_SIZE 8 * 1024 * 1024 +#define SB_U16_LO(A) ((A)&0xff) +#define SB_U16_HI(A) ((A >> 8) & 0xff) + +// Extract bits from a bitfield +#define SB_BFE(VALUE, BITOFFSET, SIZE) \ + (((VALUE) >> (BITOFFSET)) & ((2 << (SIZE)) - 1)) +#define SB_MODE_RESET 0 +#define SB_MODE_PAUSE 1 +#define SB_MODE_RUN 2 +#define SB_MODE_STEP 3 + +// Draw and process scroll bar style edition controls + +typedef struct { + int run_mode; // [0: Reset, 1: Pause, 2: Run, 3: Step ] + int step_instructions; // Number of instructions to advance while stepping +} sb_emu_state_t; + +typedef struct { + // Registers + uint16_t af, bc, de, hl, sp, pc; +} sb_gb_cpu_t; + +typedef struct { + uint8_t data[65536]; +} sb_gb_mem_t; + +typedef struct { + uint8_t data[MAX_CARTRIDGE_SIZE]; + char title[17]; + bool game_boy_color; + uint8_t type; + int rom_size; + int ram_size; +} sb_gb_cartridge_t; + +typedef struct { + sb_gb_cpu_t cpu; + sb_gb_mem_t mem; + sb_gb_cartridge_t cart; +} sb_gb_t; + +sb_emu_state_t emu_state = {0}; +sb_gb_t gb_state = {0xffff}; + +uint16_t sb_read16(sb_gb_t *gb, int addr) { + return *(uint16_t *)(gb->mem.data + addr); +} +uint8_t sb_read8(sb_gb_t *gb, int addr) { return gb->mem.data[addr]; } + +Rectangle sb_inside_rect_after_padding(Rectangle outside_rect, int padding) { + Rectangle rect_inside = outside_rect; + rect_inside.x += padding; + rect_inside.y += padding; + rect_inside.width -= padding * 2; + rect_inside.height -= padding * 2; + return rect_inside; +} +void sb_vertical_adv(Rectangle outside_rect, int advance, int y_padd, + Rectangle *rect_top, Rectangle *rect_bottom) { + *rect_top = outside_rect; + rect_top->height = advance; + *rect_bottom = outside_rect; + rect_bottom->y += advance + y_padd; + rect_bottom->height -= advance + y_padd; +} + +Rectangle sb_draw_emu_state(Rectangle rect, sb_emu_state_t *emu_state) { + + Rectangle inside_rect = sb_inside_rect_after_padding(rect, GUI_PADDING); + Rectangle widget_rect; + + sb_vertical_adv(inside_rect, GUI_ROW_HEIGHT, GUI_PADDING, &widget_rect, + &inside_rect); + widget_rect.width = + widget_rect.width / 4 - GuiGetStyle(TOGGLE, GROUP_PADDING) * 3 / 4; + emu_state->run_mode = + GuiToggleGroup(widget_rect, "Reset;Pause;Run;Step", emu_state->run_mode); + + sb_vertical_adv(inside_rect, GUI_LABEL_HEIGHT, GUI_LABEL_PADDING, + &widget_rect, &inside_rect); + + GuiLabel(widget_rect, "Instructions to Step"); + sb_vertical_adv(inside_rect, GUI_ROW_HEIGHT, GUI_PADDING, &widget_rect, + &inside_rect); + + int label_width = GetTextWidth("Instructions to Step"); + static bool edit_step_instructions = false; + if (GuiSpinner(widget_rect, "", &emu_state->step_instructions, 1, 0x7fffffff, + edit_step_instructions)) + edit_step_instructions = !edit_step_instructions; + + Rectangle state_rect, adv_rect; + sb_vertical_adv(rect, inside_rect.y - rect.y, GUI_PADDING, &state_rect, + &adv_rect); + GuiGroupBox(state_rect, TextFormat("Emulator State [FPS: %i]", GetFPS())); + return adv_rect; +} +Rectangle sb_draw_reg_state(Rectangle rect, const char *group_name, + const char **register_names, int *values) { + Rectangle inside_rect = sb_inside_rect_after_padding(rect, GUI_PADDING); + Rectangle widget_rect; + while (*register_names) { + sb_vertical_adv(inside_rect, GUI_LABEL_HEIGHT, GUI_PADDING + 5, + &widget_rect, &inside_rect); + GuiLabel(widget_rect, *register_names); + int w = (inside_rect.width - GUI_PADDING * 2) / 3; + widget_rect.x += w; + GuiLabel(widget_rect, TextFormat("0x%X", *values)); + + widget_rect.x += w + GUI_PADDING * 2; + GuiLabel(widget_rect, TextFormat("%i", *values)); + + ++register_names; + ++values; + } + + Rectangle state_rect, adv_rect; + sb_vertical_adv(rect, inside_rect.y - rect.y, GUI_PADDING, &state_rect, + &adv_rect); + return adv_rect; +} + +Rectangle sb_draw_flag_state(Rectangle rect, const char *group_name, + const char **register_names, bool *values) { + Rectangle inside_rect = sb_inside_rect_after_padding(rect, GUI_PADDING); + Rectangle widget_rect; + while (*register_names) { + sb_vertical_adv(inside_rect, GUI_LABEL_HEIGHT, GUI_PADDING + 5, + &widget_rect, &inside_rect); + widget_rect.width = GUI_PADDING; + widget_rect.height = GUI_PADDING; + + GuiCheckBox( + widget_rect, + TextFormat("%s (%s)", *register_names, (*values) ? "true" : "false"), + *values); + ++register_names; + ++values; + } + + Rectangle state_rect, adv_rect; + sb_vertical_adv(rect, inside_rect.y - rect.y, GUI_PADDING, &state_rect, + &adv_rect); + return adv_rect; +} +Rectangle sb_draw_instructions(Rectangle rect, sb_gb_cpu_t *cpu_state, + sb_gb_t *gb) { + Rectangle inside_rect = sb_inside_rect_after_padding(rect, GUI_PADDING); + Rectangle widget_rect; + for (int i = -3; i < 4; ++i) { + sb_vertical_adv(inside_rect, GUI_LABEL_HEIGHT, GUI_PADDING + 5, + &widget_rect, &inside_rect); + int pc_render = i + cpu_state->pc; + + if (pc_render < 0) { + widget_rect.x += 80; + + GuiLabel(widget_rect, "INVALID"); + } else { + if (i == 0) + GuiLabel(widget_rect, "PC->"); + widget_rect.x += 30; + GuiLabel(widget_rect, TextFormat("%04X", pc_render)); + widget_rect.x += 50; + int opcode = sb_read8(gb, pc_render); + GuiLabel(widget_rect, sb_decode_table[opcode].opcode_name); + ; + widget_rect.x += 100; + GuiLabel(widget_rect, TextFormat("(%02x)", sb_read8(gb, pc_render))); + widget_rect.x += 50; + } + } + Rectangle state_rect, adv_rect; + sb_vertical_adv(rect, inside_rect.y - rect.y, GUI_PADDING, &state_rect, + &adv_rect); + GuiGroupBox(state_rect, "Instructions"); + return adv_rect; +} +Rectangle sb_draw_cartridge_state(Rectangle rect, + sb_gb_cartridge_t *cart_state) { + + Rectangle inside_rect = sb_inside_rect_after_padding(rect, GUI_PADDING); + Rectangle widget_rect; + + sb_vertical_adv(inside_rect, GUI_LABEL_HEIGHT, GUI_PADDING, &widget_rect, + &inside_rect); + GuiLabel(widget_rect, TextFormat("Title: %s", cart_state->title)); + + sb_vertical_adv(inside_rect, GUI_LABEL_HEIGHT, GUI_PADDING + 10, &widget_rect, + &inside_rect); + + Rectangle wr = widget_rect; + wr.width = GUI_PADDING; + wr.height = GUI_PADDING; + + GuiCheckBox(wr, + TextFormat("Game Boy Color (%s)", + (cart_state->game_boy_color) ? "true" : "false"), + cart_state->game_boy_color); + + sb_vertical_adv(inside_rect, GUI_LABEL_HEIGHT, GUI_PADDING + 5, &widget_rect, + &inside_rect); + GuiLabel(widget_rect, TextFormat("Cart Type: %d", cart_state->type)); + + sb_vertical_adv(inside_rect, GUI_LABEL_HEIGHT, GUI_PADDING + 5, &widget_rect, + &inside_rect); + GuiLabel(widget_rect, TextFormat("ROM Size: %d", cart_state->rom_size)); + + sb_vertical_adv(inside_rect, GUI_LABEL_HEIGHT, GUI_PADDING + 5, &widget_rect, + &inside_rect); + GuiLabel(widget_rect, TextFormat("RAM Size: %d", cart_state->ram_size)); + + Rectangle state_rect, adv_rect; + sb_vertical_adv(rect, inside_rect.y - rect.y, GUI_PADDING, &state_rect, + &adv_rect); + GuiGroupBox(state_rect, "Cartridge State (Drag and Drop .GBC to Load ROM)"); + return adv_rect; +} +Rectangle sb_draw_cpu_state(Rectangle rect, sb_gb_cpu_t *cpu_state, + sb_gb_t *gb) { + + Rectangle inside_rect = sb_inside_rect_after_padding(rect, GUI_PADDING); + Rectangle widget_rect; + + const char *register_names_16b[] = {"AF", "BC", "DE", "HL", "SP", "PC", NULL}; + + int register_values_16b[] = {cpu_state->af, cpu_state->bc, cpu_state->de, + cpu_state->hl, cpu_state->sp, cpu_state->pc}; + + const char *register_names_8b[] = {"A", "F", "B", "C", "D", + "E", "H", "L", NULL}; + + int register_values_8b[] = { + SB_U16_HI(cpu_state->af), SB_U16_LO(cpu_state->af), + SB_U16_HI(cpu_state->bc), SB_U16_LO(cpu_state->bc), + SB_U16_HI(cpu_state->de), SB_U16_LO(cpu_state->de), + SB_U16_HI(cpu_state->hl), SB_U16_LO(cpu_state->hl), + }; + + const char *flag_names[] = {"Z", "N", "H", "C", NULL}; + + bool flag_values[] = { + SB_BFE(cpu_state->af, 7, 1), // Z + SB_BFE(cpu_state->af, 6, 1), // N + SB_BFE(cpu_state->af, 5, 1), // H + SB_BFE(cpu_state->af, 4, 1), // C + }; + // Split registers into three rects horizontally + { + Rectangle in_rect[3]; + const char *sections[] = {"16-bit Registers", "8-bit Registers", "Flags"}; + int orig_y = inside_rect.y; + int x_off = 0; + for (int i = 0; i < 3; ++i) { + in_rect[i] = inside_rect; + in_rect[i].width = inside_rect.width / 3 - GUI_PADDING * 2 / 3; + in_rect[i].x += x_off; + x_off += in_rect[i].width + GUI_PADDING; + } + in_rect[0] = sb_draw_reg_state(in_rect[0], "16-bit Registers", + register_names_16b, register_values_16b); + + in_rect[1] = sb_draw_reg_state(in_rect[1], "8-bit Registers", + register_names_8b, register_values_8b); + + in_rect[2] = + sb_draw_flag_state(in_rect[2], "Flags", flag_names, flag_values); + for (int i = 0; i < 3; ++i) { + if (inside_rect.y < in_rect[i].y) + inside_rect.y = in_rect[i].y; + } + for (int i = 0; i < 3; ++i) { + in_rect[i].height = inside_rect.y - orig_y - GUI_PADDING; + in_rect[i].y = orig_y; + GuiGroupBox(in_rect[i], sections[i]); + } + + inside_rect.height -= inside_rect.y - orig_y; + } + + inside_rect = sb_draw_instructions(inside_rect, cpu_state, gb); + + Rectangle state_rect, adv_rect; + sb_vertical_adv(rect, inside_rect.y - rect.y, GUI_PADDING, &state_rect, + &adv_rect); + GuiGroupBox(state_rect, "CPU State"); + return adv_rect; +} +void sb_draw_sidebar(Rectangle rect) { + GuiPanel(rect); + Rectangle rect_inside = sb_inside_rect_after_padding(rect, GUI_PADDING); + + rect_inside = sb_draw_emu_state(rect_inside, &emu_state); + rect_inside = sb_draw_cartridge_state(rect_inside, &gb_state.cart); + rect_inside = sb_draw_cpu_state(rect_inside, &gb_state.cpu, &gb_state); + if (emu_state.run_mode == SB_MODE_STEP) { + emu_state.run_mode = SB_MODE_PAUSE; + gb_state.cpu.pc++; + } + if (emu_state.run_mode == SB_MODE_RUN) { + gb_state.cpu.pc++; + } + if (emu_state.run_mode == SB_MODE_RESET) { + gb_state.cpu.pc = 0x100; + } +} + +Rectangle panelRec = {20, 40, 200, 150}; +Rectangle panelContentRec = {0, 0, 340, 340}; +Vector2 panelScroll = {99, -20}; + +bool showContentArea = true; +void UpdateDrawFrame() { + if (IsFileDropped()) { + int count = 0; + char **files = GetDroppedFiles(&count); + if (count > 0) { + unsigned int bytes = 0; + unsigned char *data = LoadFileData(files[0], &bytes); + printf("Dropped File: %s, %d bytes\n", files[0], bytes); + + for (size_t i = 0; i < bytes; ++i) { + gb_state.mem.data[i] = gb_state.cart.data[i] = data[i]; + } + // Copy Header + for (int i = 0; i < 11; ++i) { + gb_state.cart.title[i] = gb_state.cart.data[i + 0x134]; + } + // TODO PGB Mode(Values with Bit 7 set, and either Bit 2 or 3 set) + gb_state.cart.game_boy_color = + SB_BFE(gb_state.cart.data[0x143], 7, 1) == 1; + gb_state.cart.type = gb_state.cart.data[0x147]; + + switch (gb_state.cart.data[0x148]) { + case 0x0: + gb_state.cart.rom_size = 32 * 1024; + break; + case 0x1: + gb_state.cart.rom_size = 64 * 1024; + break; + case 0x2: + gb_state.cart.rom_size = 128 * 1024; + break; + case 0x3: + gb_state.cart.rom_size = 256 * 1024; + break; + case 0x4: + gb_state.cart.rom_size = 512 * 1024; + break; + case 0x5: + gb_state.cart.rom_size = 1024 * 1024; + break; + case 0x6: + gb_state.cart.rom_size = 2 * 1024 * 1024; + break; + case 0x7: + gb_state.cart.rom_size = 4 * 1024 * 1024; + break; + case 0x8: + gb_state.cart.rom_size = 8 * 1024 * 1024; + break; + case 0x52: + gb_state.cart.rom_size = 1.1 * 1024 * 1024; + break; + case 0x53: + gb_state.cart.rom_size = 1.2 * 1024 * 1024; + break; + case 0x54: + gb_state.cart.rom_size = 1.5 * 1024 * 1024; + break; + default: + gb_state.cart.rom_size = 32 * 1024; + break; + } + + switch (gb_state.cart.data[0x149]) { + case 0x0: + gb_state.cart.ram_size = 0; + break; + case 0x1: + gb_state.cart.ram_size = 0; + break; + case 0x2: + gb_state.cart.ram_size = 8 * 1024; + break; + case 0x3: + gb_state.cart.ram_size = 32 * 1024; + break; + case 0x4: + gb_state.cart.ram_size = 128 * 1024; + break; + case 0x5: + gb_state.cart.ram_size = 64 * 1024; + break; + default: + break; + } + emu_state.run_mode = SB_MODE_RESET; + + UnloadFileData(data); + } + ClearDroppedFiles(); + } + + // Bouncing ball logic + ballPosition.x += ballSpeed.x * GetFrameTime() * 60.; + ballPosition.y += ballSpeed.y * GetFrameTime() * 60.; + if ((ballPosition.x >= (GetScreenWidth() - ballRadius)) || + (ballPosition.x <= ballRadius)) + ballSpeed.x *= -1.0f; + if ((ballPosition.y >= (GetScreenHeight() - ballRadius)) || + (ballPosition.y <= ballRadius)) + ballSpeed.y *= -1.0f; + //----------------------------------------------------- + + // Draw + //----------------------------------------------------- + BeginDrawing(); + + if (IsWindowState(FLAG_WINDOW_TRANSPARENT)) + ClearBackground(BLANK); + else + ClearBackground(RAYWHITE); + + DrawCircleV(ballPosition, ballRadius, MAROON); + DrawRectangleLinesEx( + (Rectangle){0, 0, (float)GetScreenWidth(), (float)GetScreenHeight()}, 4, + RAYWHITE); + + DrawCircleV(GetMousePosition(), 10, DARKBLUE); + + sb_draw_sidebar((Rectangle){0, 0, 400, GetScreenHeight()}); + + EndDrawing(); +} + +int main(void) { + // Initialization + //--------------------------------------------------------- + const int screenWidth = 1200; + const int screenHeight = 700; + + // Set configuration flags for window creation + SetConfigFlags(FLAG_VSYNC_HINT | FLAG_WINDOW_HIGHDPI | FLAG_WINDOW_RESIZABLE); + InitWindow(screenWidth, screenHeight, "raylib [core] example - window flags"); + +#if defined(PLATFORM_WEB) + emscripten_set_main_loop(UpdateDrawFrame, 0, 1); +#else + SetTargetFPS(60); + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + UpdateDrawFrame(); + } +#endif + + CloseWindow(); // Close window and OpenGL context + + return 0; +} diff --git a/src/raygui.h b/src/raygui.h new file mode 100644 index 000000000..b24118866 --- /dev/null +++ b/src/raygui.h @@ -0,0 +1,4476 @@ +/******************************************************************************************* + * + * raygui v2.9-dev - A simple and easy-to-use immediate-mode gui library + * + * DESCRIPTION: + * + * raygui is a tools-dev-focused immediate-mode-gui library based on raylib + *but also available as a standalone library, as long as input and drawing + *functions are provided. + * + * Controls provided: + * + * # Container/separators Controls + * - WindowBox + * - GroupBox + * - Line + * - Panel + * + * # Basic Controls + * - Label + * - Button + * - LabelButton --> Label + * - ImageButton --> Button + * - ImageButtonEx --> Button + * - Toggle + * - ToggleGroup --> Toggle + * - CheckBox + * - ComboBox + * - DropdownBox + * - TextBox + * - TextBoxMulti + * - ValueBox --> TextBox + * - Spinner --> Button, ValueBox + * - Slider + * - SliderBar --> Slider + * - ProgressBar + * - StatusBar + * - ScrollBar + * - ScrollPanel + * - DummyRec + * - Grid + * + * # Advance Controls + * - ListView + * - ColorPicker --> ColorPanel, ColorBarHue + * - MessageBox --> Window, Label, Button + * - TextInputBox --> Window, Label, TextBox, Button + * + * It also provides a set of functions for styling the controls based on its + *properties (size, color). + * + * CONFIGURATION: + * + * #define RAYGUI_IMPLEMENTATION + * Generates the implementation of the library into the included file. + * If not defined, the library is in header only mode and can be included + *in other headers or source files without problems. But only ONE file should + *hold the implementation. + * + * #define RAYGUI_STATIC (defined by default) + * The generated implementation will stay private inside implementation + *file and all internal symbols and functions will only be visible inside that + *file. + * + * #define RAYGUI_STANDALONE + * Avoid raylib.h header inclusion in this file. Data types defined on + *raylib are defined internally in the library and input management and drawing + *functions must be provided by the user (check library implementation for + *further details). + * + * #define RAYGUI_SUPPORT_ICONS + * Includes riconsdata.h header defining a set of 128 icons (binary + *format) to be used on multiple controls and following raygui styles + * + * + * VERSIONS HISTORY: + * 2.9 (17-Mar-2021) Removed tooltip API + * 2.8 (03-May-2020) Centralized rectangles drawing to GuiDrawRectangle() + * 2.7 (20-Feb-2020) Added possible tooltips API + * 2.6 (09-Sep-2019) ADDED: GuiTextInputBox() + * REDESIGNED: GuiListView*(), GuiDropdownBox(), + *GuiSlider*(), GuiProgressBar(), GuiMessageBox() REVIEWED: GuiTextBox(), + *GuiSpinner(), GuiValueBox(), GuiLoadStyle() Replaced property INNER_PADDING by + *TEXT_PADDING, renamed some properties Added 8 new custom styles ready to use + * Multiple minor tweaks and bugs corrected + * 2.5 (28-May-2019) Implemented extended GuiTextBox(), GuiValueBox(), + *GuiSpinner() 2.3 (29-Apr-2019) Added rIcons auxiliar library and support for + *it, multiple controls reviewed Refactor all controls drawing mechanism to use + *control state 2.2 (05-Feb-2019) Added GuiScrollBar(), GuiScrollPanel(), + *reviewed GuiListView(), removed Gui*Ex() controls 2.1 (26-Dec-2018) Redesign + *of GuiCheckBox(), GuiComboBox(), GuiDropdownBox(), GuiToggleGroup() > Use + *combined text string Complete redesign of style system (breaking change) 2.0 + *(08-Nov-2018) Support controls guiLock and custom fonts, reviewed + *GuiComboBox(), GuiListView()... 1.9 (09-Oct-2018) Controls review: GuiGrid(), + *GuiTextBox(), GuiTextBoxMulti(), GuiValueBox()... 1.8 (01-May-2018) Lot of + *rework and redesign to align with rGuiStyler and rGuiLayout 1.5 (21-Jun-2017) + *Working in an improved styles system 1.4 (15-Jun-2017) Rewritten all GUI + *functions (removed useless ones) 1.3 (12-Jun-2017) Redesigned styles system + * 1.1 (01-Jun-2017) Complete review of the library + * 1.0 (07-Jun-2016) Converted to header-only by Ramon Santamaria. + * 0.9 (07-Mar-2016) Reviewed and tested by Albert Martos, Ian Eito, + *Sergio Martinez and Ramon Santamaria. 0.8 (27-Aug-2015) Initial release. + *Implemented by Kevin Gato, Daniel Nicolás and Ramon Santamaria. + * + * CONTRIBUTORS: + * Ramon Santamaria: Supervision, review, redesign, update and + *maintenance... Vlad Adrian: Complete rewrite of GuiTextBox() to support + *extended features (2019) Sergio Martinez: Review, testing (2015) and + *redesign of multiple controls (2018) Adria Arranz: Testing and + *Implementation of additional controls (2018) Jordi Jorba: Testing and + *Implementation of additional controls (2018) Albert Martos: Review and + *testing of the library (2015) Ian Eito: Review and testing of the + *library (2015) Kevin Gato: Initial implementation of basic components + *(2014) Daniel Nicolas: Initial implementation of basic components (2014) + * + * + * LICENSE: zlib/libpng + * + * Copyright (c) 2014-2020 Ramon Santamaria (@raysan5) + * + * This software is provided "as-is", without any express or implied warranty. + *In no event will the authors be held liable for any damages arising from the + *use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + *including commercial applications, and to alter it and redistribute it freely, + *subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + *claim that you wrote the original software. If you use this software in a + *product, an acknowledgment in the product documentation would be appreciated + *but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + *be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + *distribution. + * + **********************************************************************************************/ + +#ifndef RAYGUI_H +#define RAYGUI_H + +#define RAYGUI_VERSION "2.9-dev" + +#if !defined(RAYGUI_STANDALONE) +#include "raylib.h" +#endif + +// Define functions scope to be used internally (static) or externally (extern) +// to the module including this file +#if defined(_WIN32) +// Microsoft attibutes to tell compiler that symbols are imported/exported from +// a .dll +#if defined(BUILD_LIBTYPE_SHARED) +#define RAYGUIDEF \ + __declspec( \ + dllexport) // We are building raygui as a Win32 shared library (.dll) +#elif defined(USE_LIBTYPE_SHARED) +#define RAYGUIDEF \ + __declspec(dllimport) // We are using raygui as a Win32 shared library (.dll) +#else +#define RAYGUIDEF // We are building or using raygui as a static library +#endif +#else +#define RAYGUIDEF // We are building or using raygui as a static library (or + // Linux shared library) +#endif + +#if !defined(RAYGUI_MALLOC) && !defined(RAYGUI_CALLOC) && !defined(RAYGUI_FREE) +#include // Required for: malloc(), calloc(), free() +#endif + +// Allow custom memory allocators +#ifndef RAYGUI_MALLOC +#define RAYGUI_MALLOC(sz) malloc(sz) +#endif +#ifndef RAYGUI_CALLOC +#define RAYGUI_CALLOC(n, sz) calloc(n, sz) +#endif +#ifndef RAYGUI_FREE +#define RAYGUI_FREE(p) free(p) +#endif + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +#define NUM_CONTROLS 16 // Number of standard controls +#define NUM_PROPS_DEFAULT 16 // Number of standard properties +#define NUM_PROPS_EXTENDED 8 // Number of extended properties + +#define TEXTEDIT_CURSOR_BLINK_FRAMES \ + 20 // Text edit controls cursor blink timming + +#if defined(__cplusplus) +extern "C" { // Prevents name mangling of functions +#endif + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +// NOTE: Some types are required for RAYGUI_STANDALONE usage +//---------------------------------------------------------------------------------- +#if defined(RAYGUI_STANDALONE) +#ifndef __cplusplus +// Boolean type +#ifndef true +typedef enum { false, true } bool; +#endif +#endif + +// Vector2 type +typedef struct Vector2 { + float x; + float y; +} Vector2; + +// Vector3 type +typedef struct Vector3 { + float x; + float y; + float z; +} Vector3; + +// Color type, RGBA (32bit) +typedef struct Color { + unsigned char r; + unsigned char g; + unsigned char b; + unsigned char a; +} Color; + +// Rectangle type +typedef struct Rectangle { + float x; + float y; + float width; + float height; +} Rectangle; + +// TODO: Texture2D type is very coupled to raylib, mostly required by +// GuiImageButton() It should be redesigned to be provided by user +typedef struct Texture2D { + unsigned int id; // OpenGL texture id + int width; // Texture base width + int height; // Texture base height + int mipmaps; // Mipmap levels, 1 by default + int format; // Data format (PixelFormat type) +} Texture2D; + +// Font character info +typedef struct CharInfo CharInfo; + +// TODO: Font type is very coupled to raylib, mostly required by GuiLoadStyle() +// It should be redesigned to be provided by user +typedef struct Font { + int baseSize; // Base size (default chars height) + int charsCount; // Number of characters + Texture2D texture; // Characters texture atlas + Rectangle *recs; // Characters rectangles in texture + CharInfo *chars; // Characters info data +} Font; +#endif + +// Style property +typedef struct GuiStyleProp { + unsigned short controlId; + unsigned short propertyId; + int propertyValue; +} GuiStyleProp; + +// Gui control state +typedef enum { + GUI_STATE_NORMAL = 0, + GUI_STATE_FOCUSED, + GUI_STATE_PRESSED, + GUI_STATE_DISABLED, +} GuiControlState; + +// Gui control text alignment +typedef enum { + GUI_TEXT_ALIGN_LEFT = 0, + GUI_TEXT_ALIGN_CENTER, + GUI_TEXT_ALIGN_RIGHT, +} GuiTextAlignment; + +// Gui controls +typedef enum { + DEFAULT = 0, + LABEL, // LABELBUTTON + BUTTON, // IMAGEBUTTON + TOGGLE, // TOGGLEGROUP + SLIDER, // SLIDERBAR + PROGRESSBAR, + CHECKBOX, + COMBOBOX, + DROPDOWNBOX, + TEXTBOX, // TEXTBOXMULTI + VALUEBOX, + SPINNER, + LISTVIEW, + COLORPICKER, + SCROLLBAR, + STATUSBAR +} GuiControl; + +// Gui base properties for every control +typedef enum { + BORDER_COLOR_NORMAL = 0, + BASE_COLOR_NORMAL, + TEXT_COLOR_NORMAL, + BORDER_COLOR_FOCUSED, + BASE_COLOR_FOCUSED, + TEXT_COLOR_FOCUSED, + BORDER_COLOR_PRESSED, + BASE_COLOR_PRESSED, + TEXT_COLOR_PRESSED, + BORDER_COLOR_DISABLED, + BASE_COLOR_DISABLED, + TEXT_COLOR_DISABLED, + BORDER_WIDTH, + TEXT_PADDING, + TEXT_ALIGNMENT, + RESERVED +} GuiControlProperty; + +// Gui extended properties depend on control +// NOTE: We reserve a fixed size of additional properties per control + +// DEFAULT properties +typedef enum { + TEXT_SIZE = 16, + TEXT_SPACING, + LINE_COLOR, + BACKGROUND_COLOR, +} GuiDefaultProperty; + +// Label +// typedef enum { } GuiLabelProperty; + +// Button +// typedef enum { } GuiButtonProperty; + +// Toggle / ToggleGroup +typedef enum { + GROUP_PADDING = 16, +} GuiToggleProperty; + +// Slider / SliderBar +typedef enum { SLIDER_WIDTH = 16, SLIDER_PADDING } GuiSliderProperty; + +// ProgressBar +typedef enum { + PROGRESS_PADDING = 16, +} GuiProgressBarProperty; + +// CheckBox +typedef enum { CHECK_PADDING = 16 } GuiCheckBoxProperty; + +// ComboBox +typedef enum { + COMBO_BUTTON_WIDTH = 16, + COMBO_BUTTON_PADDING +} GuiComboBoxProperty; + +// DropdownBox +typedef enum { + ARROW_PADDING = 16, + DROPDOWN_ITEMS_PADDING +} GuiDropdownBoxProperty; + +// TextBox / TextBoxMulti / ValueBox / Spinner +typedef enum { + TEXT_INNER_PADDING = 16, + TEXT_LINES_PADDING, + COLOR_SELECTED_FG, + COLOR_SELECTED_BG +} GuiTextBoxProperty; + +// Spinner +typedef enum { + SPIN_BUTTON_WIDTH = 16, + SPIN_BUTTON_PADDING, +} GuiSpinnerProperty; + +// ScrollBar +typedef enum { + ARROWS_SIZE = 16, + ARROWS_VISIBLE, + SCROLL_SLIDER_PADDING, + SCROLL_SLIDER_SIZE, + SCROLL_PADDING, + SCROLL_SPEED, +} GuiScrollBarProperty; + +// ScrollBar side +typedef enum { SCROLLBAR_LEFT_SIDE = 0, SCROLLBAR_RIGHT_SIDE } GuiScrollBarSide; + +// ListView +typedef enum { + LIST_ITEMS_HEIGHT = 16, + LIST_ITEMS_PADDING, + SCROLLBAR_WIDTH, + SCROLLBAR_SIDE, +} GuiListViewProperty; + +// ColorPicker +typedef enum { + COLOR_SELECTOR_SIZE = 16, + HUEBAR_WIDTH, // Right hue bar width + HUEBAR_PADDING, // Right hue bar separation from panel + HUEBAR_SELECTOR_HEIGHT, // Right hue bar selector height + HUEBAR_SELECTOR_OVERFLOW // Right hue bar selector overflow +} GuiColorPickerProperty; + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +// ... + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- + +// State modification functions +RAYGUIDEF void GuiEnable(void); // Enable gui controls (global state) +RAYGUIDEF void GuiDisable(void); // Disable gui controls (global state) +RAYGUIDEF void GuiLock(void); // Lock gui controls (global state) +RAYGUIDEF void GuiUnlock(void); // Unlock gui controls (global state) +RAYGUIDEF void GuiFade(float alpha); // Set gui controls alpha (global state), + // alpha goes from 0.0f to 1.0f +RAYGUIDEF void GuiSetState(int state); // Set gui state (global state) +RAYGUIDEF int GuiGetState(void); // Get gui state (global state) + +// Font set/get functions +RAYGUIDEF void GuiSetFont(Font font); // Set gui custom font (global state) +RAYGUIDEF Font GuiGetFont(void); // Get gui custom font (global state) + +// Style set/get functions +RAYGUIDEF void GuiSetStyle(int control, int property, + int value); // Set one style property +RAYGUIDEF int GuiGetStyle(int control, int property); // Get one style property + +// Container/separator controls, useful for controls organization +RAYGUIDEF bool GuiWindowBox( + Rectangle bounds, + const char *title); // Window Box control, shows a window that can be closed +RAYGUIDEF void +GuiGroupBox(Rectangle bounds, + const char *text); // Group Box control with text name +RAYGUIDEF void +GuiLine(Rectangle bounds, + const char *text); // Line separator control, could contain text +RAYGUIDEF void +GuiPanel(Rectangle bounds); // Panel control, useful to group controls +RAYGUIDEF Rectangle GuiScrollPanel(Rectangle bounds, Rectangle content, + Vector2 *scroll); // Scroll Panel control + +// Basic controls set +RAYGUIDEF void GuiLabel(Rectangle bounds, + const char *text); // Label control, shows text +RAYGUIDEF bool +GuiButton(Rectangle bounds, + const char *text); // Button control, returns true when clicked +RAYGUIDEF bool GuiLabelButton( + Rectangle bounds, + const char *text); // Label button control, show true when clicked +RAYGUIDEF bool GuiImageButton( + Rectangle bounds, const char *text, + Texture2D texture); // Image button control, returns true when clicked +RAYGUIDEF bool +GuiImageButtonEx(Rectangle bounds, const char *text, Texture2D texture, + Rectangle texSource); // Image button extended control, returns + // true when clicked +RAYGUIDEF bool +GuiToggle(Rectangle bounds, const char *text, + bool active); // Toggle Button control, returns true when active +RAYGUIDEF int +GuiToggleGroup(Rectangle bounds, const char *text, + int active); // Toggle Group control, returns active toggle index +RAYGUIDEF bool +GuiCheckBox(Rectangle bounds, const char *text, + bool checked); // Check Box control, returns true when active +RAYGUIDEF int +GuiComboBox(Rectangle bounds, const char *text, + int active); // Combo Box control, returns selected item index +RAYGUIDEF bool +GuiDropdownBox(Rectangle bounds, const char *text, int *active, + bool editMode); // Dropdown Box control, returns selected item +RAYGUIDEF bool +GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, + int maxValue, + bool editMode); // Spinner control, returns selected value +RAYGUIDEF bool GuiValueBox( + Rectangle bounds, const char *text, int *value, int minValue, int maxValue, + bool editMode); // Value Box control, updates input text with numbers +RAYGUIDEF bool +GuiTextBox(Rectangle bounds, char *text, int textSize, + bool editMode); // Text Box control, updates input text +RAYGUIDEF bool +GuiTextBoxMulti(Rectangle bounds, char *text, int textSize, + bool editMode); // Text Box control with multiple lines +RAYGUIDEF float +GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, + float value, float minValue, + float maxValue); // Slider control, returns selected value +RAYGUIDEF float +GuiSliderBar(Rectangle bounds, const char *textLeft, const char *textRight, + float value, float minValue, + float maxValue); // Slider Bar control, returns selected value +RAYGUIDEF float GuiProgressBar( + Rectangle bounds, const char *textLeft, const char *textRight, float value, + float minValue, + float maxValue); // Progress Bar control, shows current progress value +RAYGUIDEF void +GuiStatusBar(Rectangle bounds, + const char *text); // Status Bar control, shows info text +RAYGUIDEF void GuiDummyRec(Rectangle bounds, + const char *text); // Dummy control for placeholders +RAYGUIDEF int GuiScrollBar(Rectangle bounds, int value, int minValue, + int maxValue); // Scroll Bar control +RAYGUIDEF Vector2 GuiGrid(Rectangle bounds, float spacing, + int subdivs); // Grid control + +// Advance controls set +RAYGUIDEF int +GuiListView(Rectangle bounds, const char *text, int *scrollIndex, + int active); // List View control, returns selected list item index +RAYGUIDEF int GuiListViewEx(Rectangle bounds, const char **text, int count, + int *focus, int *scrollIndex, + int active); // List View with extended parameters +RAYGUIDEF int +GuiMessageBox(Rectangle bounds, const char *title, const char *message, + const char *buttons); // Message Box control, displays a message +RAYGUIDEF int +GuiTextInputBox(Rectangle bounds, const char *title, const char *message, + const char *buttons, + char *text); // Text Input Box control, ask for text +RAYGUIDEF Color +GuiColorPicker(Rectangle bounds, + Color color); // Color Picker control (multiple color controls) +RAYGUIDEF Color GuiColorPanel(Rectangle bounds, + Color color); // Color Panel control +RAYGUIDEF float GuiColorBarAlpha(Rectangle bounds, + float alpha); // Color Bar Alpha control +RAYGUIDEF float GuiColorBarHue(Rectangle bounds, + float value); // Color Bar Hue control + +// Styles loading functions +RAYGUIDEF void GuiLoadStyle(const char *fileName); // Load style file (.rgs) +RAYGUIDEF void +GuiLoadStyleDefault(void); // Load style default over global style + +/* +typedef GuiStyle (unsigned int *) +RAYGUIDEF GuiStyle LoadGuiStyle(const char *fileName); // Load style +from file (.rgs) RAYGUIDEF void UnloadGuiStyle(GuiStyle style); // Unload style +*/ + +RAYGUIDEF const char * +GuiIconText(int iconId, + const char *text); // Get text with icon id prepended (if supported) + +#if defined(RAYGUI_SUPPORT_ICONS) +// Gui icons functionality +RAYGUIDEF void GuiDrawIcon(int iconId, Vector2 position, int pixelSize, + Color color); + +RAYGUIDEF unsigned int *GuiGetIcons(void); // Get full icons data pointer +RAYGUIDEF unsigned int *GuiGetIconData(int iconId); // Get icon bit data +RAYGUIDEF void GuiSetIconData(int iconId, + unsigned int *data); // Set icon bit data + +RAYGUIDEF void GuiSetIconPixel(int iconId, int x, + int y); // Set icon pixel value +RAYGUIDEF void GuiClearIconPixel(int iconId, int x, + int y); // Clear icon pixel value +RAYGUIDEF bool GuiCheckIconPixel(int iconId, int x, + int y); // Check icon pixel value +#endif + +#if defined(__cplusplus) +} // Prevents name mangling of functions +#endif + +#endif // RAYGUI_H + +/*********************************************************************************** + * + * RAYGUI IMPLEMENTATION + * + ************************************************************************************/ + +#if defined(RAYGUI_IMPLEMENTATION) + +#if defined(RAYGUI_SUPPORT_ICONS) +#define RICONS_IMPLEMENTATION +#include "ricons.h" // Required for: raygui icons data +#endif + +#include // Required for: roundf() on GuiColorPicker() +#include // Required for: FILE, fopen(), fclose(), fprintf(), feof(), fscanf(), vsprintf() +#include // Required for: strlen() on GuiTextBox() + +#if defined(RAYGUI_STANDALONE) +#include // Required for: va_list, va_start(), vfprintf(), va_end() +#endif + +#ifdef __cplusplus +#define RAYGUI_CLITERAL(name) name +#else +#define RAYGUI_CLITERAL(name) (name) +#endif + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +//... + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +// Gui control property style color element +typedef enum { BORDER = 0, BASE, TEXT, OTHER } GuiPropertyElement; + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +static GuiControlState guiState = GUI_STATE_NORMAL; + +static Font guiFont = { + 0}; // Gui current font (WARNING: highly coupled to raylib) +static bool guiLocked = false; // Gui lock state (no inputs processed) +static float guiAlpha = 1.0f; // Gui element transpacency on drawing + +// Global gui style array (allocated on data segment by default) +// NOTE: In raygui we manage a single int array with all the possible style +// properties. When a new style is loaded, it loads over the global style... but +// default gui style could always be recovered with GuiLoadStyleDefault() +static unsigned int + guiStyle[NUM_CONTROLS * (NUM_PROPS_DEFAULT + NUM_PROPS_EXTENDED)] = {0}; +static bool guiStyleLoaded = + false; // Style loaded flag for lazy style initialization + +//---------------------------------------------------------------------------------- +// Standalone Mode Functions Declaration +// +// NOTE: raygui depend on some raylib input and drawing functions +// To use raygui as standalone library, below functions must be defined by the +// user +//---------------------------------------------------------------------------------- +#if defined(RAYGUI_STANDALONE) + +#define KEY_RIGHT 262 +#define KEY_LEFT 263 +#define KEY_DOWN 264 +#define KEY_UP 265 +#define KEY_BACKSPACE 259 +#define KEY_ENTER 257 + +#define MOUSE_LEFT_BUTTON 0 + +// Input required functions +//------------------------------------------------------------------------------- +static Vector2 GetMousePosition(void); +static int GetMouseWheelMove(void); +static bool IsMouseButtonDown(int button); +static bool IsMouseButtonPressed(int button); +static bool IsMouseButtonReleased(int button); + +static bool IsKeyDown(int key); +static bool IsKeyPressed(int key); +static int +GetCharPressed(void); // -- GuiTextBox(), GuiTextBoxMulti(), GuiValueBox() +//------------------------------------------------------------------------------- + +// Drawing required functions +//------------------------------------------------------------------------------- +static void DrawRectangle(int x, int y, int width, int height, + Color color); // -- GuiDrawRectangle(), GuiDrawIcon() + +static void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, + Color col3, + Color col4); // -- GuiColorPicker() +static void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, + Color color); // -- GuiDropdownBox(), GuiScrollBar() +static void DrawTextureRec(Texture2D texture, Rectangle sourceRec, + Vector2 position, + Color tint); // -- GuiImageButtonEx() + +static void DrawTextRec(Font font, const char *text, Rectangle rec, + float fontSize, float spacing, bool wordWrap, + Color tint); // -- GuiTextBoxMulti() +//------------------------------------------------------------------------------- + +// Text required functions +//------------------------------------------------------------------------------- +static Font GetFontDefault(void); // -- GuiLoadStyleDefault() +static Vector2 +MeasureTextEx(Font font, const char *text, float fontSize, + float spacing); // -- GetTextWidth(), GuiTextBoxMulti() +static void DrawTextEx(Font font, const char *text, Vector2 position, + float fontSize, float spacing, + Color tint); // -- GuiDrawText() + +static Font LoadFontEx(const char *fileName, int fontSize, int *fontChars, + int charsCount); // -- GuiLoadStyle() +static char *LoadFileText(const char *fileName); // -- GuiLoadStyle() +static const char *GetDirectoryPath(const char *filePath); // -- GuiLoadStyle() +//------------------------------------------------------------------------------- + +// raylib functions already implemented in raygui +//------------------------------------------------------------------------------- +static Color +GetColor(int hexValue); // Returns a Color struct from hexadecimal value +static int ColorToInt(Color color); // Returns hexadecimal value for a Color +static Color +Fade(Color color, + float alpha); // Color fade-in or fade-out, alpha goes from 0.0f to 1.0f +static bool +CheckCollisionPointRec(Vector2 point, + Rectangle rec); // Check if point is inside rectangle +static const char * +TextFormat(const char *text, + ...); // Formatting of text with variables to 'embed' +static const char **TextSplit(const char *text, char delimiter, + int *count); // Split text into multiple strings +static int TextToInteger(const char *text); // Get integer value from text + +static void +DrawRectangleGradientV(int posX, int posY, int width, int height, Color color1, + Color color2); // Draw rectangle vertical gradient +//------------------------------------------------------------------------------- + +#endif // RAYGUI_STANDALONE + +//---------------------------------------------------------------------------------- +// Module specific Functions Declaration +//---------------------------------------------------------------------------------- +static int +GetTextWidth(const char *text); // Gui get text width using default font +static Rectangle +GetTextBounds(int control, + Rectangle bounds); // Get text bounds considering control bounds +static const char * +GetTextIcon(const char *text, + int *iconId); // Get text icon if provided and move text cursor + +static void GuiDrawText(const char *text, Rectangle bounds, int alignment, + Color tint); // Gui draw text using default font +static void +GuiDrawRectangle(Rectangle rec, int borderWidth, Color borderColor, + Color color); // Gui draw rectangle using default raygui style + +static const char ** +GuiTextSplit(const char *text, int *count, + int *textRow); // Split controls text into multiple strings +static Vector3 +ConvertHSVtoRGB(Vector3 hsv); // Convert color data from HSV to RGB +static Vector3 +ConvertRGBtoHSV(Vector3 rgb); // Convert color data from RGB to HSV + +//---------------------------------------------------------------------------------- +// Gui Setup Functions Definition +//---------------------------------------------------------------------------------- +// Enable gui global state +void GuiEnable(void) { guiState = GUI_STATE_NORMAL; } + +// Disable gui global state +void GuiDisable(void) { guiState = GUI_STATE_DISABLED; } + +// Lock gui global state +void GuiLock(void) { guiLocked = true; } + +// Unlock gui global state +void GuiUnlock(void) { guiLocked = false; } + +// Set gui controls alpha global state +void GuiFade(float alpha) { + if (alpha < 0.0f) + alpha = 0.0f; + else if (alpha > 1.0f) + alpha = 1.0f; + + guiAlpha = alpha; +} + +// Set gui state (global state) +void GuiSetState(int state) { guiState = (GuiControlState)state; } + +// Get gui state (global state) +int GuiGetState(void) { return guiState; } + +// Set custom gui font +// NOTE: Font loading/unloading is external to raygui +void GuiSetFont(Font font) { + if (font.texture.id > 0) { + // NOTE: If we try to setup a font but default style has not been + // lazily loaded before, it will be overwritten, so we need to force + // default style loading first + if (!guiStyleLoaded) + GuiLoadStyleDefault(); + + guiFont = font; + GuiSetStyle(DEFAULT, TEXT_SIZE, font.baseSize); + } +} + +// Get custom gui font +Font GuiGetFont(void) { return guiFont; } + +// Set control style property value +void GuiSetStyle(int control, int property, int value) { + if (!guiStyleLoaded) + GuiLoadStyleDefault(); + guiStyle[control * (NUM_PROPS_DEFAULT + NUM_PROPS_EXTENDED) + property] = + value; + + // Default properties are propagated to all controls + if ((control == 0) && (property < NUM_PROPS_DEFAULT)) { + for (int i = 1; i < NUM_CONTROLS; i++) + guiStyle[i * (NUM_PROPS_DEFAULT + NUM_PROPS_EXTENDED) + property] = value; + } +} + +// Get control style property value +int GuiGetStyle(int control, int property) { + if (!guiStyleLoaded) + GuiLoadStyleDefault(); + return guiStyle[control * (NUM_PROPS_DEFAULT + NUM_PROPS_EXTENDED) + + property]; +} + +//---------------------------------------------------------------------------------- +// Gui Controls Functions Definition +//---------------------------------------------------------------------------------- + +// Window Box control +bool GuiWindowBox(Rectangle bounds, const char *title) { +// NOTE: This define is also used by GuiMessageBox() and GuiTextInputBox() +#define WINDOW_STATUSBAR_HEIGHT 22 + + // GuiControlState state = guiState; + bool clicked = false; + + int statusBarHeight = + WINDOW_STATUSBAR_HEIGHT + 2 * GuiGetStyle(STATUSBAR, BORDER_WIDTH); + statusBarHeight += (statusBarHeight % 2); + + Rectangle statusBar = {bounds.x, bounds.y, bounds.width, + (float)statusBarHeight}; + if (bounds.height < statusBarHeight * 2.0f) + bounds.height = statusBarHeight * 2.0f; + + Rectangle windowPanel = {bounds.x, bounds.y + (float)statusBarHeight - 1, + bounds.width, + bounds.height - (float)statusBarHeight}; + Rectangle closeButtonRec = { + statusBar.x + statusBar.width - GuiGetStyle(STATUSBAR, BORDER_WIDTH) - 20, + statusBar.y + statusBarHeight / 2.0f - 18.0f / 2.0f, 18, 18}; + + // Update control + //-------------------------------------------------------------------- + // NOTE: Logic is directly managed by button + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiStatusBar(statusBar, title); // Draw window header as status bar + GuiPanel(windowPanel); // Draw window base + + // Draw window close button + int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH); + int tempTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); + GuiSetStyle(BUTTON, BORDER_WIDTH, 1); + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER); +#if defined(RAYGUI_SUPPORT_ICONS) + clicked = GuiButton(closeButtonRec, GuiIconText(RICON_CROSS_SMALL, NULL)); +#else + clicked = GuiButton(closeButtonRec, "x"); +#endif + GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth); + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlignment); + //-------------------------------------------------------------------- + + return clicked; +} + +// Group Box control with text name +void GuiGroupBox(Rectangle bounds, const char *text) { +#define GROUPBOX_LINE_THICK 1 +#define GROUPBOX_TEXT_PADDING 10 + + GuiControlState state = guiState; + + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle( + RAYGUI_CLITERAL(Rectangle){bounds.x, bounds.y, GROUPBOX_LINE_THICK, + bounds.height}, + 0, BLANK, + Fade(GetColor(GuiGetStyle(DEFAULT, (state == GUI_STATE_DISABLED) + ? BORDER_COLOR_DISABLED + : LINE_COLOR)), + guiAlpha)); + GuiDrawRectangle( + RAYGUI_CLITERAL(Rectangle){bounds.x, bounds.y + bounds.height - 1, + bounds.width, GROUPBOX_LINE_THICK}, + 0, BLANK, + Fade(GetColor(GuiGetStyle(DEFAULT, (state == GUI_STATE_DISABLED) + ? BORDER_COLOR_DISABLED + : LINE_COLOR)), + guiAlpha)); + GuiDrawRectangle( + RAYGUI_CLITERAL(Rectangle){bounds.x + bounds.width - 1, bounds.y, + GROUPBOX_LINE_THICK, bounds.height}, + 0, BLANK, + Fade(GetColor(GuiGetStyle(DEFAULT, (state == GUI_STATE_DISABLED) + ? BORDER_COLOR_DISABLED + : LINE_COLOR)), + guiAlpha)); + + GuiLine(RAYGUI_CLITERAL(Rectangle){bounds.x, bounds.y, bounds.width, 1}, + text); + //-------------------------------------------------------------------- +} + +// Line control +void GuiLine(Rectangle bounds, const char *text) { +#define LINE_TEXT_PADDING 10 + + GuiControlState state = guiState; + + Color color = Fade(GetColor(GuiGetStyle(DEFAULT, (state == GUI_STATE_DISABLED) + ? BORDER_COLOR_DISABLED + : LINE_COLOR)), + guiAlpha); + + // Draw control + //-------------------------------------------------------------------- + if (text == NULL) + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){bounds.x, + bounds.y + bounds.height / 2, + bounds.width, 1}, + 0, BLANK, color); + else { + Rectangle textBounds = {0}; + textBounds.width = (float)GetTextWidth(text); // TODO: Consider text icon + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = bounds.x + LINE_TEXT_PADDING; + textBounds.y = bounds.y - (float)GuiGetStyle(DEFAULT, TEXT_SIZE) / 2; + + // Draw line with embedded text label: "--- text --------------" + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){bounds.x, bounds.y, + LINE_TEXT_PADDING - 2, 1}, + 0, BLANK, color); + GuiLabel(textBounds, text); + GuiDrawRectangle( + RAYGUI_CLITERAL(Rectangle){ + bounds.x + LINE_TEXT_PADDING + textBounds.width + 4, bounds.y, + bounds.width - textBounds.width - LINE_TEXT_PADDING - 4, 1}, + 0, BLANK, color); + } + //-------------------------------------------------------------------- +} + +// Panel control +void GuiPanel(Rectangle bounds) { +#define PANEL_BORDER_WIDTH 1 + + GuiControlState state = guiState; + + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle( + bounds, PANEL_BORDER_WIDTH, + Fade(GetColor(GuiGetStyle(DEFAULT, (state == GUI_STATE_DISABLED) + ? BORDER_COLOR_DISABLED + : LINE_COLOR)), + guiAlpha), + Fade(GetColor(GuiGetStyle(DEFAULT, (state == GUI_STATE_DISABLED) + ? BASE_COLOR_DISABLED + : BACKGROUND_COLOR)), + guiAlpha)); + //-------------------------------------------------------------------- +} + +// Scroll Panel control +Rectangle GuiScrollPanel(Rectangle bounds, Rectangle content, Vector2 *scroll) { + GuiControlState state = guiState; + + Vector2 scrollPos = {0.0f, 0.0f}; + if (scroll != NULL) + scrollPos = *scroll; + + bool hasHorizontalScrollBar = + (content.width > bounds.width - 2 * GuiGetStyle(DEFAULT, BORDER_WIDTH)) + ? true + : false; + bool hasVerticalScrollBar = + (content.height > bounds.height - 2 * GuiGetStyle(DEFAULT, BORDER_WIDTH)) + ? true + : false; + + // Recheck to account for the other scrollbar being visible + if (!hasHorizontalScrollBar) + hasHorizontalScrollBar = + (hasVerticalScrollBar && + (content.width > + (bounds.width - 2 * GuiGetStyle(DEFAULT, BORDER_WIDTH) - + GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH)))) + ? true + : false; + if (!hasVerticalScrollBar) + hasVerticalScrollBar = + (hasHorizontalScrollBar && + (content.height > + (bounds.height - 2 * GuiGetStyle(DEFAULT, BORDER_WIDTH) - + GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH)))) + ? true + : false; + + const int horizontalScrollBarWidth = + hasHorizontalScrollBar ? GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH) : 0; + const int verticalScrollBarWidth = + hasVerticalScrollBar ? GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH) : 0; + const Rectangle horizontalScrollBar = { + (float)((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE) + ? (float)bounds.x + verticalScrollBarWidth + : (float)bounds.x) + + GuiGetStyle(DEFAULT, BORDER_WIDTH), + (float)bounds.y + bounds.height - horizontalScrollBarWidth - + GuiGetStyle(DEFAULT, BORDER_WIDTH), + (float)bounds.width - verticalScrollBarWidth - + 2 * GuiGetStyle(DEFAULT, BORDER_WIDTH), + (float)horizontalScrollBarWidth}; + const Rectangle verticalScrollBar = { + (float)((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE) + ? (float)bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH) + : (float)bounds.x + bounds.width - verticalScrollBarWidth - + GuiGetStyle(DEFAULT, BORDER_WIDTH)), + (float)bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), + (float)verticalScrollBarWidth, + (float)bounds.height - horizontalScrollBarWidth - + 2 * GuiGetStyle(DEFAULT, BORDER_WIDTH)}; + + // Calculate view area (area without the scrollbars) + Rectangle view = + (GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE) + ? RAYGUI_CLITERAL( + Rectangle){bounds.x + verticalScrollBarWidth + + GuiGetStyle(DEFAULT, BORDER_WIDTH), + bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), + bounds.width - + 2 * GuiGetStyle(DEFAULT, BORDER_WIDTH) - + verticalScrollBarWidth, + bounds.height - + 2 * GuiGetStyle(DEFAULT, BORDER_WIDTH) - + horizontalScrollBarWidth} + : RAYGUI_CLITERAL(Rectangle){ + bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH), + bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), + bounds.width - 2 * GuiGetStyle(DEFAULT, BORDER_WIDTH) - + verticalScrollBarWidth, + bounds.height - 2 * GuiGetStyle(DEFAULT, BORDER_WIDTH) - + horizontalScrollBarWidth}; + + // Clip view area to the actual content size + if (view.width > content.width) + view.width = content.width; + if (view.height > content.height) + view.height = content.height; + + // TODO: Review! + const float horizontalMin = + hasHorizontalScrollBar + ? ((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE) + ? (float)-verticalScrollBarWidth + : 0) - + (float)GuiGetStyle(DEFAULT, BORDER_WIDTH) + : (((float)GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == + SCROLLBAR_LEFT_SIDE) + ? (float)-verticalScrollBarWidth + : 0) - + (float)GuiGetStyle(DEFAULT, BORDER_WIDTH); + const float horizontalMax = + hasHorizontalScrollBar + ? content.width - bounds.width + (float)verticalScrollBarWidth + + GuiGetStyle(DEFAULT, BORDER_WIDTH) - + (((float)GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == + SCROLLBAR_LEFT_SIDE) + ? (float)verticalScrollBarWidth + : 0) + : (float)-GuiGetStyle(DEFAULT, BORDER_WIDTH); + const float verticalMin = hasVerticalScrollBar + ? (float)-GuiGetStyle(DEFAULT, BORDER_WIDTH) + : (float)-GuiGetStyle(DEFAULT, BORDER_WIDTH); + const float verticalMax = hasVerticalScrollBar + ? content.height - bounds.height + + (float)horizontalScrollBarWidth + + (float)GuiGetStyle(DEFAULT, BORDER_WIDTH) + : (float)-GuiGetStyle(DEFAULT, BORDER_WIDTH); + + // Update control + //-------------------------------------------------------------------- + if ((state != GUI_STATE_DISABLED) && !guiLocked) { + Vector2 mousePoint = GetMousePosition(); + + // Check button state + if (CheckCollisionPointRec(mousePoint, bounds)) { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + state = GUI_STATE_PRESSED; + else + state = GUI_STATE_FOCUSED; + + if (hasHorizontalScrollBar) { + if (IsKeyDown(KEY_RIGHT)) + scrollPos.x -= GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + if (IsKeyDown(KEY_LEFT)) + scrollPos.x += GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + } + + if (hasVerticalScrollBar) { + if (IsKeyDown(KEY_DOWN)) + scrollPos.y -= GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + if (IsKeyDown(KEY_UP)) + scrollPos.y += GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + } + + float wheelMove = GetMouseWheelMove(); + + // Horizontal scroll (Shift + Mouse wheel) + if (hasHorizontalScrollBar && + (IsKeyDown(KEY_LEFT_SHIFT) || IsKeyDown(KEY_RIGHT_SHIFT))) + scrollPos.x += wheelMove * 20; + else + scrollPos.y += wheelMove * 20; // Vertical scroll + } + } + + // Normalize scroll values + if (scrollPos.x > -horizontalMin) + scrollPos.x = -horizontalMin; + if (scrollPos.x < -horizontalMax) + scrollPos.x = -horizontalMax; + if (scrollPos.y > -verticalMin) + scrollPos.y = -verticalMin; + if (scrollPos.y < -verticalMax) + scrollPos.y = -verticalMax; + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle( + bounds, 0, BLANK, + GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR))); // Draw background + + // Save size of the scrollbar slider + const int slider = GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE); + + // Draw horizontal scrollbar if visible + if (hasHorizontalScrollBar) { + // Change scrollbar slider size to show the diff in size between the content + // width and the widget width + GuiSetStyle( + SCROLLBAR, SCROLL_SLIDER_SIZE, + (int)(((bounds.width - 2 * GuiGetStyle(DEFAULT, BORDER_WIDTH) - + verticalScrollBarWidth) / + (int)content.width) * + ((int)bounds.width - 2 * GuiGetStyle(DEFAULT, BORDER_WIDTH) - + verticalScrollBarWidth))); + scrollPos.x = (float)-GuiScrollBar(horizontalScrollBar, (int)-scrollPos.x, + (int)horizontalMin, (int)horizontalMax); + } + + // Draw vertical scrollbar if visible + if (hasVerticalScrollBar) { + // Change scrollbar slider size to show the diff in size between the content + // height and the widget height + GuiSetStyle( + SCROLLBAR, SCROLL_SLIDER_SIZE, + (int)(((bounds.height - 2 * GuiGetStyle(DEFAULT, BORDER_WIDTH) - + horizontalScrollBarWidth) / + (int)content.height) * + ((int)bounds.height - 2 * GuiGetStyle(DEFAULT, BORDER_WIDTH) - + horizontalScrollBarWidth))); + scrollPos.y = (float)-GuiScrollBar(verticalScrollBar, (int)-scrollPos.y, + (int)verticalMin, (int)verticalMax); + } + + // Draw detail corner rectangle if both scroll bars are visible + if (hasHorizontalScrollBar && hasVerticalScrollBar) { + Rectangle corner = { + (GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE) + ? (bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH) + 2) + : (horizontalScrollBar.x + horizontalScrollBar.width + 2), + verticalScrollBar.y + verticalScrollBar.height + 2, + (float)horizontalScrollBarWidth - 4, (float)verticalScrollBarWidth - 4}; + GuiDrawRectangle( + corner, 0, BLANK, + Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT + (state * 3))), guiAlpha)); + } + + // Draw scrollbar lines depending on current state + GuiDrawRectangle( + bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), + Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER + (state * 3))), guiAlpha), + BLANK); + + // Set scrollbar slider size back to the way it was before + GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, slider); + //-------------------------------------------------------------------- + + if (scroll != NULL) + *scroll = scrollPos; + + return view; +} + +// Label control +void GuiLabel(Rectangle bounds, const char *text) { + GuiControlState state = guiState; + + // Update control + //-------------------------------------------------------------------- + // ... + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawText(text, GetTextBounds(LABEL, bounds), + GuiGetStyle(LABEL, TEXT_ALIGNMENT), + Fade(GetColor(GuiGetStyle(LABEL, (state == GUI_STATE_DISABLED) + ? TEXT_COLOR_DISABLED + : TEXT_COLOR_NORMAL)), + guiAlpha)); + //-------------------------------------------------------------------- +} + +// Button control, returns true when clicked +bool GuiButton(Rectangle bounds, const char *text) { + GuiControlState state = guiState; + bool pressed = false; + + // Update control + //-------------------------------------------------------------------- + if ((state != GUI_STATE_DISABLED) && !guiLocked) { + Vector2 mousePoint = GetMousePosition(); + + // Check button state + if (CheckCollisionPointRec(mousePoint, bounds)) { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + state = GUI_STATE_PRESSED; + else + state = GUI_STATE_FOCUSED; + + if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) + pressed = true; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle( + bounds, GuiGetStyle(BUTTON, BORDER_WIDTH), + Fade(GetColor(GuiGetStyle(BUTTON, BORDER + (state * 3))), guiAlpha), + Fade(GetColor(GuiGetStyle(BUTTON, BASE + (state * 3))), guiAlpha)); + GuiDrawText( + text, GetTextBounds(BUTTON, bounds), GuiGetStyle(BUTTON, TEXT_ALIGNMENT), + Fade(GetColor(GuiGetStyle(BUTTON, TEXT + (state * 3))), guiAlpha)); + //------------------------------------------------------------------ + + return pressed; +} + +// Label button control +bool GuiLabelButton(Rectangle bounds, const char *text) { + GuiControlState state = guiState; + bool pressed = false; + + // NOTE: We force bounds.width to be all text + float textWidth = + MeasureTextEx(guiFont, text, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)) + .x; + if (bounds.width < textWidth) + bounds.width = textWidth; + + // Update control + //-------------------------------------------------------------------- + if ((state != GUI_STATE_DISABLED) && !guiLocked) { + Vector2 mousePoint = GetMousePosition(); + + // Check checkbox state + if (CheckCollisionPointRec(mousePoint, bounds)) { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + state = GUI_STATE_PRESSED; + else + state = GUI_STATE_FOCUSED; + + if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) + pressed = true; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawText(text, GetTextBounds(LABEL, bounds), + GuiGetStyle(LABEL, TEXT_ALIGNMENT), + Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state * 3))), guiAlpha)); + //-------------------------------------------------------------------- + + return pressed; +} + +// Image button control, returns true when clicked +bool GuiImageButton(Rectangle bounds, const char *text, Texture2D texture) { + return GuiImageButtonEx(bounds, text, texture, + RAYGUI_CLITERAL(Rectangle){0, 0, (float)texture.width, + (float)texture.height}); +} + +// Image button control, returns true when clicked +bool GuiImageButtonEx(Rectangle bounds, const char *text, Texture2D texture, + Rectangle texSource) { + GuiControlState state = guiState; + bool clicked = false; + + // Update control + //-------------------------------------------------------------------- + if ((state != GUI_STATE_DISABLED) && !guiLocked) { + Vector2 mousePoint = GetMousePosition(); + + // Check button state + if (CheckCollisionPointRec(mousePoint, bounds)) { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + state = GUI_STATE_PRESSED; + else if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) + clicked = true; + else + state = GUI_STATE_FOCUSED; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle( + bounds, GuiGetStyle(BUTTON, BORDER_WIDTH), + Fade(GetColor(GuiGetStyle(BUTTON, BORDER + (state * 3))), guiAlpha), + Fade(GetColor(GuiGetStyle(BUTTON, BASE + (state * 3))), guiAlpha)); + + if (text != NULL) + GuiDrawText( + text, GetTextBounds(BUTTON, bounds), + GuiGetStyle(BUTTON, TEXT_ALIGNMENT), + Fade(GetColor(GuiGetStyle(BUTTON, TEXT + (state * 3))), guiAlpha)); + if (texture.id > 0) + DrawTextureRec( + texture, texSource, + RAYGUI_CLITERAL(Vector2){ + bounds.x + bounds.width / 2 - texSource.width / 2, + bounds.y + bounds.height / 2 - texSource.height / 2}, + Fade(GetColor(GuiGetStyle(BUTTON, TEXT + (state * 3))), guiAlpha)); + //------------------------------------------------------------------ + + return clicked; +} + +// Toggle Button control, returns true when active +bool GuiToggle(Rectangle bounds, const char *text, bool active) { + GuiControlState state = guiState; + + // Update control + //-------------------------------------------------------------------- + if ((state != GUI_STATE_DISABLED) && !guiLocked) { + Vector2 mousePoint = GetMousePosition(); + + // Check toggle button state + if (CheckCollisionPointRec(mousePoint, bounds)) { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + state = GUI_STATE_PRESSED; + else if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) { + state = GUI_STATE_NORMAL; + active = !active; + } else + state = GUI_STATE_FOCUSED; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + if (state == GUI_STATE_NORMAL) { + GuiDrawRectangle( + bounds, GuiGetStyle(TOGGLE, BORDER_WIDTH), + Fade(GetColor(GuiGetStyle(TOGGLE, (active ? BORDER_COLOR_PRESSED + : (BORDER + state * 3)))), + guiAlpha), + Fade(GetColor(GuiGetStyle( + TOGGLE, (active ? BASE_COLOR_PRESSED : (BASE + state * 3)))), + guiAlpha)); + GuiDrawText( + text, GetTextBounds(TOGGLE, bounds), + GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), + Fade(GetColor(GuiGetStyle( + TOGGLE, (active ? TEXT_COLOR_PRESSED : (TEXT + state * 3)))), + guiAlpha)); + } else { + GuiDrawRectangle( + bounds, GuiGetStyle(TOGGLE, BORDER_WIDTH), + Fade(GetColor(GuiGetStyle(TOGGLE, BORDER + state * 3)), guiAlpha), + Fade(GetColor(GuiGetStyle(TOGGLE, BASE + state * 3)), guiAlpha)); + GuiDrawText( + text, GetTextBounds(TOGGLE, bounds), + GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), + Fade(GetColor(GuiGetStyle(TOGGLE, TEXT + state * 3)), guiAlpha)); + } + //-------------------------------------------------------------------- + + return active; +} + +// Toggle Group control, returns toggled button index +int GuiToggleGroup(Rectangle bounds, const char *text, int active) { +#if !defined(TOGGLEGROUP_MAX_ELEMENTS) +#define TOGGLEGROUP_MAX_ELEMENTS 32 +#endif + + float initBoundsX = bounds.x; + + // Get substrings items from text (items pointers) + int rows[TOGGLEGROUP_MAX_ELEMENTS] = {0}; + int itemsCount = 0; + const char **items = GuiTextSplit(text, &itemsCount, rows); + + int prevRow = rows[0]; + + for (int i = 0; i < itemsCount; i++) { + if (prevRow != rows[i]) { + bounds.x = initBoundsX; + bounds.y += (bounds.height + GuiGetStyle(TOGGLE, GROUP_PADDING)); + prevRow = rows[i]; + } + + if (i == active) + GuiToggle(bounds, items[i], true); + else if (GuiToggle(bounds, items[i], false) == true) + active = i; + + bounds.x += (bounds.width + GuiGetStyle(TOGGLE, GROUP_PADDING)); + } + + return active; +} + +// Check Box control, returns true when active +bool GuiCheckBox(Rectangle bounds, const char *text, bool checked) { + GuiControlState state = guiState; + + Rectangle textBounds = {0}; + + if (text != NULL) { + textBounds.width = (float)GetTextWidth(text); + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = + bounds.x + bounds.width + GuiGetStyle(CHECKBOX, TEXT_PADDING); + textBounds.y = + bounds.y + bounds.height / 2 - GuiGetStyle(DEFAULT, TEXT_SIZE) / 2; + if (GuiGetStyle(CHECKBOX, TEXT_ALIGNMENT) == GUI_TEXT_ALIGN_LEFT) + textBounds.x = + bounds.x - textBounds.width - GuiGetStyle(CHECKBOX, TEXT_PADDING); + } + + // Update control + //-------------------------------------------------------------------- + if ((state != GUI_STATE_DISABLED) && !guiLocked) { + Vector2 mousePoint = GetMousePosition(); + + Rectangle totalBounds = { + (GuiGetStyle(CHECKBOX, TEXT_ALIGNMENT) == GUI_TEXT_ALIGN_LEFT) + ? textBounds.x + : bounds.x, + bounds.y, + bounds.width + textBounds.width + GuiGetStyle(CHECKBOX, TEXT_PADDING), + bounds.height, + }; + + // Check checkbox state + if (CheckCollisionPointRec(mousePoint, totalBounds)) { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + state = GUI_STATE_PRESSED; + else + state = GUI_STATE_FOCUSED; + + if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) + checked = !checked; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle( + bounds, GuiGetStyle(CHECKBOX, BORDER_WIDTH), + Fade(GetColor(GuiGetStyle(CHECKBOX, BORDER + (state * 3))), guiAlpha), + BLANK); + + if (checked) { + Rectangle check = { + bounds.x + GuiGetStyle(CHECKBOX, BORDER_WIDTH) + + GuiGetStyle(CHECKBOX, CHECK_PADDING), + bounds.y + GuiGetStyle(CHECKBOX, BORDER_WIDTH) + + GuiGetStyle(CHECKBOX, CHECK_PADDING), + bounds.width - 2 * (GuiGetStyle(CHECKBOX, BORDER_WIDTH) + + GuiGetStyle(CHECKBOX, CHECK_PADDING)), + bounds.height - 2 * (GuiGetStyle(CHECKBOX, BORDER_WIDTH) + + GuiGetStyle(CHECKBOX, CHECK_PADDING))}; + GuiDrawRectangle( + check, 0, BLANK, + Fade(GetColor(GuiGetStyle(CHECKBOX, TEXT + state * 3)), guiAlpha)); + } + + if (text != NULL) + GuiDrawText( + text, textBounds, + (GuiGetStyle(CHECKBOX, TEXT_ALIGNMENT) == GUI_TEXT_ALIGN_RIGHT) + ? GUI_TEXT_ALIGN_LEFT + : GUI_TEXT_ALIGN_RIGHT, + Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state * 3))), guiAlpha)); + //-------------------------------------------------------------------- + + return checked; +} + +// Combo Box control, returns selected item index +int GuiComboBox(Rectangle bounds, const char *text, int active) { + GuiControlState state = guiState; + + bounds.width -= (GuiGetStyle(COMBOBOX, COMBO_BUTTON_WIDTH) + + GuiGetStyle(COMBOBOX, COMBO_BUTTON_PADDING)); + + Rectangle selector = {(float)bounds.x + bounds.width + + GuiGetStyle(COMBOBOX, COMBO_BUTTON_PADDING), + (float)bounds.y, + (float)GuiGetStyle(COMBOBOX, COMBO_BUTTON_WIDTH), + (float)bounds.height}; + + // Get substrings items from text (items pointers, lengths and count) + int itemsCount = 0; + const char **items = GuiTextSplit(text, &itemsCount, NULL); + + if (active < 0) + active = 0; + else if (active > itemsCount - 1) + active = itemsCount - 1; + + // Update control + //-------------------------------------------------------------------- + if ((state != GUI_STATE_DISABLED) && !guiLocked && (itemsCount > 1)) { + Vector2 mousePoint = GetMousePosition(); + + if (CheckCollisionPointRec(mousePoint, bounds) || + CheckCollisionPointRec(mousePoint, selector)) { + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { + active += 1; + if (active >= itemsCount) + active = 0; + } + + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + state = GUI_STATE_PRESSED; + else + state = GUI_STATE_FOCUSED; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + // Draw combo box main + GuiDrawRectangle( + bounds, GuiGetStyle(COMBOBOX, BORDER_WIDTH), + Fade(GetColor(GuiGetStyle(COMBOBOX, BORDER + (state * 3))), guiAlpha), + Fade(GetColor(GuiGetStyle(COMBOBOX, BASE + (state * 3))), guiAlpha)); + GuiDrawText( + items[active], GetTextBounds(COMBOBOX, bounds), + GuiGetStyle(COMBOBOX, TEXT_ALIGNMENT), + Fade(GetColor(GuiGetStyle(COMBOBOX, TEXT + (state * 3))), guiAlpha)); + + // Draw selector using a custom button + // NOTE: BORDER_WIDTH and TEXT_ALIGNMENT forced values + int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH); + int tempTextAlign = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); + GuiSetStyle(BUTTON, BORDER_WIDTH, 1); + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER); + + GuiButton(selector, TextFormat("%i/%i", active + 1, itemsCount)); + + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlign); + GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth); + //-------------------------------------------------------------------- + + return active; +} + +// Dropdown Box control +// NOTE: Returns mouse click +bool GuiDropdownBox(Rectangle bounds, const char *text, int *active, + bool editMode) { + GuiControlState state = guiState; + int itemSelected = *active; + int itemFocused = -1; + + // Get substrings items from text (items pointers, lengths and count) + int itemsCount = 0; + const char **items = GuiTextSplit(text, &itemsCount, NULL); + + Rectangle boundsOpen = bounds; + boundsOpen.height = + (itemsCount + 1) * + (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_PADDING)); + + Rectangle itemBounds = bounds; + + bool pressed = false; // Check mouse button pressed + + // Update control + //-------------------------------------------------------------------- + if ((state != GUI_STATE_DISABLED) && !guiLocked && (itemsCount > 1)) { + Vector2 mousePoint = GetMousePosition(); + + if (editMode) { + state = GUI_STATE_PRESSED; + + // Check if mouse has been pressed or released outside limits + if (!CheckCollisionPointRec(mousePoint, boundsOpen)) { + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON) || + IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) + pressed = true; + } + + // Check if already selected item has been pressed again + if (CheckCollisionPointRec(mousePoint, bounds) && + IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + pressed = true; + + // Check focused and selected item + for (int i = 0; i < itemsCount; i++) { + // Update item rectangle y position for next item + itemBounds.y += + (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_PADDING)); + + if (CheckCollisionPointRec(mousePoint, itemBounds)) { + itemFocused = i; + if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) { + itemSelected = i; + pressed = true; // Item selected, change to editMode = false + } + break; + } + } + + itemBounds = bounds; + } else { + if (CheckCollisionPointRec(mousePoint, bounds)) { + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { + pressed = true; + state = GUI_STATE_PRESSED; + } else + state = GUI_STATE_FOCUSED; + } + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + if (editMode) + GuiPanel(boundsOpen); + + GuiDrawRectangle( + bounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), + Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BORDER + state * 3)), guiAlpha), + Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BASE + state * 3)), guiAlpha)); + GuiDrawText( + items[itemSelected], GetTextBounds(DEFAULT, bounds), + GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), + Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + state * 3)), guiAlpha)); + + if (editMode) { + // Draw visible items + for (int i = 0; i < itemsCount; i++) { + // Update item rectangle y position for next item + itemBounds.y += + (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_PADDING)); + + if (i == itemSelected) { + GuiDrawRectangle( + itemBounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), + Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BORDER_COLOR_PRESSED)), + guiAlpha), + Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_PRESSED)), + guiAlpha)); + GuiDrawText(items[i], GetTextBounds(DEFAULT, itemBounds), + GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), + Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_PRESSED)), + guiAlpha)); + } else if (i == itemFocused) { + GuiDrawRectangle( + itemBounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), + Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BORDER_COLOR_FOCUSED)), + guiAlpha), + Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_FOCUSED)), + guiAlpha)); + GuiDrawText(items[i], GetTextBounds(DEFAULT, itemBounds), + GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), + Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_FOCUSED)), + guiAlpha)); + } else + GuiDrawText(items[i], GetTextBounds(DEFAULT, itemBounds), + GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), + Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_NORMAL)), + guiAlpha)); + } + } + + // TODO: Avoid this function, use icon instead or 'v' + DrawTriangle( + RAYGUI_CLITERAL(Vector2){bounds.x + bounds.width - + GuiGetStyle(DROPDOWNBOX, ARROW_PADDING), + bounds.y + bounds.height / 2 - 2}, + RAYGUI_CLITERAL(Vector2){bounds.x + bounds.width - + GuiGetStyle(DROPDOWNBOX, ARROW_PADDING) + 5, + bounds.y + bounds.height / 2 - 2 + 5}, + RAYGUI_CLITERAL(Vector2){bounds.x + bounds.width - + GuiGetStyle(DROPDOWNBOX, ARROW_PADDING) + 10, + bounds.y + bounds.height / 2 - 2}, + Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state * 3))), guiAlpha)); + + // GuiDrawText("v", RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - + // GuiGetStyle(DROPDOWNBOX, ARROW_PADDING), bounds.y + bounds.height/2 - 2, + // 10, 10 }, + // GUI_TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(DROPDOWNBOX, + // TEXT + (state*3))), guiAlpha)); + //-------------------------------------------------------------------- + + *active = itemSelected; + return pressed; +} + +// Text Box control, updates input text +// NOTE 1: Requires static variables: framesCounter +// NOTE 2: Returns if KEY_ENTER pressed (useful for data validation) +bool GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) { + static int framesCounter = 0; // Required for blinking cursor + + GuiControlState state = guiState; + bool pressed = false; + + Rectangle cursor = { + bounds.x + GuiGetStyle(TEXTBOX, TEXT_PADDING) + GetTextWidth(text) + 2, + bounds.y + bounds.height / 2 - GuiGetStyle(DEFAULT, TEXT_SIZE), 1, + (float)GuiGetStyle(DEFAULT, TEXT_SIZE) * 2}; + + // Update control + //-------------------------------------------------------------------- + if ((state != GUI_STATE_DISABLED) && !guiLocked) { + Vector2 mousePoint = GetMousePosition(); + + if (editMode) { + state = GUI_STATE_PRESSED; + framesCounter++; + + int key = GetCharPressed(); // Returns codepoint as Unicode + int keyCount = (int)strlen(text); + + // Only allow keys in range [32..125] + if (keyCount < (textSize - 1)) { + float maxWidth = + (bounds.width - (GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING) * 2)); + + if ((GetTextWidth(text) < + (maxWidth - GuiGetStyle(DEFAULT, TEXT_SIZE))) && + (key >= 32)) { + int byteLength = 0; + const char *textUtf8 = CodepointToUtf8(key, &byteLength); + + for (int i = 0; i < byteLength; i++) { + text[keyCount] = textUtf8[i]; + keyCount++; + } + + text[keyCount] = '\0'; + } + } + + // Delete text + if (keyCount > 0) { + if (IsKeyPressed(KEY_BACKSPACE)) { + keyCount--; + text[keyCount] = '\0'; + framesCounter = 0; + if (keyCount < 0) + keyCount = 0; + } else if (IsKeyDown(KEY_BACKSPACE)) { + if ((framesCounter > TEXTEDIT_CURSOR_BLINK_FRAMES) && + (framesCounter % 2) == 0) + keyCount--; + text[keyCount] = '\0'; + if (keyCount < 0) + keyCount = 0; + } + } + + if (IsKeyPressed(KEY_ENTER) || + (!CheckCollisionPointRec(mousePoint, bounds) && + IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) + pressed = true; + + // Check text alignment to position cursor properly + int textAlignment = GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT); + if (textAlignment == GUI_TEXT_ALIGN_CENTER) + cursor.x = bounds.x + GetTextWidth(text) / 2 + bounds.width / 2 + 1; + else if (textAlignment == GUI_TEXT_ALIGN_RIGHT) + cursor.x = + bounds.x + bounds.width - GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING); + } else { + if (CheckCollisionPointRec(mousePoint, bounds)) { + state = GUI_STATE_FOCUSED; + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + pressed = true; + } + } + + if (pressed) + framesCounter = 0; + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + if (state == GUI_STATE_PRESSED) { + GuiDrawRectangle( + bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), + Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state * 3))), guiAlpha), + Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_PRESSED)), guiAlpha)); + + // Draw blinking cursor + if (editMode && ((framesCounter / 20) % 2 == 0)) + GuiDrawRectangle( + cursor, 0, BLANK, + Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED)), guiAlpha)); + } else if (state == GUI_STATE_DISABLED) { + GuiDrawRectangle( + bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), + Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state * 3))), guiAlpha), + Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED)), guiAlpha)); + } else + GuiDrawRectangle( + bounds, 1, + Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state * 3))), guiAlpha), + BLANK); + + GuiDrawText( + text, GetTextBounds(TEXTBOX, bounds), + GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), + Fade(GetColor(GuiGetStyle(TEXTBOX, TEXT + (state * 3))), guiAlpha)); + //-------------------------------------------------------------------- + + return pressed; +} + +// Spinner control, returns selected value +bool GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, + int maxValue, bool editMode) { + GuiControlState state = guiState; + + bool pressed = false; + int tempValue = *value; + + Rectangle spinner = {bounds.x + GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH) + + GuiGetStyle(SPINNER, SPIN_BUTTON_PADDING), + bounds.y, + bounds.width - + 2 * (GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH) + + GuiGetStyle(SPINNER, SPIN_BUTTON_PADDING)), + bounds.height}; + Rectangle leftButtonBound = {(float)bounds.x, (float)bounds.y, + (float)GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH), + (float)bounds.height}; + Rectangle rightButtonBound = { + (float)bounds.x + bounds.width - GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH), + (float)bounds.y, (float)GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH), + (float)bounds.height}; + + Rectangle textBounds = {0}; + if (text != NULL) { + textBounds.width = (float)GetTextWidth(text); + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = bounds.x + bounds.width + GuiGetStyle(SPINNER, TEXT_PADDING); + textBounds.y = + bounds.y + bounds.height / 2 - GuiGetStyle(DEFAULT, TEXT_SIZE) / 2; + if (GuiGetStyle(SPINNER, TEXT_ALIGNMENT) == GUI_TEXT_ALIGN_LEFT) + textBounds.x = + bounds.x - textBounds.width - GuiGetStyle(SPINNER, TEXT_PADDING); + } + + // Update control + //-------------------------------------------------------------------- + if ((state != GUI_STATE_DISABLED) && !guiLocked) { + Vector2 mousePoint = GetMousePosition(); + + // Check spinner state + if (CheckCollisionPointRec(mousePoint, bounds)) { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + state = GUI_STATE_PRESSED; + else + state = GUI_STATE_FOCUSED; + } + } + + if (!editMode) { + if (tempValue < minValue) + tempValue = minValue; + if (tempValue > maxValue) + tempValue = maxValue; + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + // TODO: Set Spinner properties for ValueBox + pressed = + GuiValueBox(spinner, NULL, &tempValue, minValue, maxValue, editMode); + + // Draw value selector custom buttons + // NOTE: BORDER_WIDTH and TEXT_ALIGNMENT forced values + int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH); + int tempTextAlign = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); + GuiSetStyle(BUTTON, BORDER_WIDTH, GuiGetStyle(SPINNER, BORDER_WIDTH)); + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER); + +#if defined(RAYGUI_SUPPORT_ICONS) + if (GuiButton(leftButtonBound, GuiIconText(RICON_ARROW_LEFT_FILL, NULL))) + tempValue--; + if (GuiButton(rightButtonBound, GuiIconText(RICON_ARROW_RIGHT_FILL, NULL))) + tempValue++; +#else + if (GuiButton(leftButtonBound, "<")) + tempValue--; + if (GuiButton(rightButtonBound, ">")) + tempValue++; +#endif + + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlign); + GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth); + + // Draw text label if provided + if (text != NULL) + GuiDrawText( + text, textBounds, + (GuiGetStyle(SPINNER, TEXT_ALIGNMENT) == GUI_TEXT_ALIGN_RIGHT) + ? GUI_TEXT_ALIGN_LEFT + : GUI_TEXT_ALIGN_RIGHT, + Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state * 3))), guiAlpha)); + //-------------------------------------------------------------------- + + *value = tempValue; + return pressed; +} + +// Value Box control, updates input text with numbers +// NOTE: Requires static variables: framesCounter +bool GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, + int maxValue, bool editMode) { +#if !defined(VALUEBOX_MAX_CHARS) +#define VALUEBOX_MAX_CHARS 32 +#endif + + static int framesCounter = 0; // Required for blinking cursor + + GuiControlState state = guiState; + bool pressed = false; + + char textValue[VALUEBOX_MAX_CHARS + 1] = "\0"; + sprintf(textValue, "%i", *value); + + Rectangle textBounds = {0}; + if (text != NULL) { + textBounds.width = (float)GetTextWidth(text); + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = + bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING); + textBounds.y = + bounds.y + bounds.height / 2 - GuiGetStyle(DEFAULT, TEXT_SIZE) / 2; + if (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == GUI_TEXT_ALIGN_LEFT) + textBounds.x = + bounds.x - textBounds.width - GuiGetStyle(VALUEBOX, TEXT_PADDING); + } + + // Update control + //-------------------------------------------------------------------- + if ((state != GUI_STATE_DISABLED) && !guiLocked) { + Vector2 mousePoint = GetMousePosition(); + + bool valueHasChanged = false; + + if (editMode) { + state = GUI_STATE_PRESSED; + + framesCounter++; + + int keyCount = (int)strlen(textValue); + + // Only allow keys in range [48..57] + if (keyCount < VALUEBOX_MAX_CHARS) { + if (GetTextWidth(textValue) < bounds.width) { + int key = GetCharPressed(); + if ((key >= 48) && (key <= 57)) { + textValue[keyCount] = (char)key; + keyCount++; + valueHasChanged = true; + } + } + } + + // Delete text + if (keyCount > 0) { + if (IsKeyPressed(KEY_BACKSPACE)) { + keyCount--; + textValue[keyCount] = '\0'; + framesCounter = 0; + if (keyCount < 0) + keyCount = 0; + valueHasChanged = true; + } else if (IsKeyDown(KEY_BACKSPACE)) { + if ((framesCounter > TEXTEDIT_CURSOR_BLINK_FRAMES) && + (framesCounter % 2) == 0) + keyCount--; + textValue[keyCount] = '\0'; + if (keyCount < 0) + keyCount = 0; + valueHasChanged = true; + } + } + + if (valueHasChanged) + *value = TextToInteger(textValue); + + if (IsKeyPressed(KEY_ENTER) || + (!CheckCollisionPointRec(mousePoint, bounds) && + IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) + pressed = true; + } else { + if (*value > maxValue) + *value = maxValue; + else if (*value < minValue) + *value = minValue; + + if (CheckCollisionPointRec(mousePoint, bounds)) { + state = GUI_STATE_FOCUSED; + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + pressed = true; + } + } + + if (pressed) + framesCounter = 0; + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + Color baseColor = BLANK; + if (state == GUI_STATE_PRESSED) + baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_PRESSED)); + else if (state == GUI_STATE_DISABLED) + baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_DISABLED)); + + // WARNING: BLANK color does not work properly with Fade() + GuiDrawRectangle( + bounds, GuiGetStyle(VALUEBOX, BORDER_WIDTH), + Fade(GetColor(GuiGetStyle(VALUEBOX, BORDER + (state * 3))), guiAlpha), + baseColor); + GuiDrawText( + textValue, GetTextBounds(VALUEBOX, bounds), GUI_TEXT_ALIGN_CENTER, + Fade(GetColor(GuiGetStyle(VALUEBOX, TEXT + (state * 3))), guiAlpha)); + + // Draw blinking cursor + if ((state == GUI_STATE_PRESSED) && + (editMode && ((framesCounter / 20) % 2 == 0))) { + // NOTE: ValueBox internal text is always centered + Rectangle cursor = { + bounds.x + GetTextWidth(textValue) / 2 + bounds.width / 2 + 2, + bounds.y + 2 * GuiGetStyle(VALUEBOX, BORDER_WIDTH), 1, + bounds.height - 4 * GuiGetStyle(VALUEBOX, BORDER_WIDTH)}; + GuiDrawRectangle( + cursor, 0, BLANK, + Fade(GetColor(GuiGetStyle(VALUEBOX, BORDER_COLOR_PRESSED)), guiAlpha)); + } + + // Draw text label if provided + if (text != NULL) + GuiDrawText( + text, textBounds, + (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == GUI_TEXT_ALIGN_RIGHT) + ? GUI_TEXT_ALIGN_LEFT + : GUI_TEXT_ALIGN_RIGHT, + Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state * 3))), guiAlpha)); + //-------------------------------------------------------------------- + + return pressed; +} + +// Text Box control with multiple lines +bool GuiTextBoxMulti(Rectangle bounds, char *text, int textSize, + bool editMode) { + static int framesCounter = 0; // Required for blinking cursor + + GuiControlState state = guiState; + bool pressed = false; + + Rectangle textAreaBounds = { + bounds.x + GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING), + bounds.y + GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING), + bounds.width - 2 * GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING), + bounds.height - 2 * GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING)}; + + // Cursor position, [x, y] values should be updated + Rectangle cursor = {0, 0, 1, (float)GuiGetStyle(DEFAULT, TEXT_SIZE) + 2}; + + int textWidth = 0; + int currentLine = 0; + + // Update control + //-------------------------------------------------------------------- + if ((state != GUI_STATE_DISABLED) && !guiLocked) { + Vector2 mousePoint = GetMousePosition(); + + if (editMode) { + state = GUI_STATE_PRESSED; + framesCounter++; + + int character = GetCharPressed(); + int keyCount = (int)strlen(text); + + // Introduce characters + if (keyCount < (textSize - 1)) { + Vector2 textSize = + MeasureTextEx(guiFont, text, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); + + if (textSize.y < + (textAreaBounds.height - GuiGetStyle(DEFAULT, TEXT_SIZE))) { + if (IsKeyPressed(KEY_ENTER)) { + text[keyCount] = '\n'; + keyCount++; + } else if (((character >= 32) && + (character < 255))) // TODO: Support Unicode inputs + { + text[keyCount] = (char)character; + keyCount++; + } + } + } + + // Delete characters + if (keyCount > 0) { + if (IsKeyPressed(KEY_BACKSPACE)) { + keyCount--; + text[keyCount] = '\0'; + framesCounter = 0; + + if (keyCount < 0) + keyCount = 0; + } else if (IsKeyDown(KEY_BACKSPACE)) { + if ((framesCounter > TEXTEDIT_CURSOR_BLINK_FRAMES) && + (framesCounter % 2) == 0) + keyCount--; + text[keyCount] = '\0'; + + if (keyCount < 0) + keyCount = 0; + } + } + + // Calculate cursor position considering text + char oneCharText[2] = {0}; + int lastBreakingPos = -1; + + for (int i = 0; i < keyCount && currentLine < keyCount; i++) { + oneCharText[0] = text[i]; + textWidth += + (GetTextWidth(oneCharText) + GuiGetStyle(DEFAULT, TEXT_SPACING)); + + if (text[i] == ' ' || text[i] == '\n') + lastBreakingPos = i; + + if (text[i] == '\n' || textWidth >= textAreaBounds.width) { + currentLine++; + textWidth = 0; + + if (lastBreakingPos > 0) + i = lastBreakingPos; + else + textWidth += (GetTextWidth(oneCharText) + + GuiGetStyle(DEFAULT, TEXT_SPACING)); + + lastBreakingPos = -1; + } + } + + cursor.x = bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH) + + GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING) + textWidth - + GuiGetStyle(DEFAULT, TEXT_SPACING); + cursor.y = bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH) + + GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING) / 2 + + ((GuiGetStyle(DEFAULT, TEXT_SIZE) + + GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING)) * + currentLine); + + // Exit edit mode + if (!CheckCollisionPointRec(mousePoint, bounds) && + IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + pressed = true; + } else { + if (CheckCollisionPointRec(mousePoint, bounds)) { + state = GUI_STATE_FOCUSED; + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + pressed = true; + } + } + + if (pressed) + framesCounter = 0; // Reset blinking cursor + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + if (state == GUI_STATE_PRESSED) { + GuiDrawRectangle( + bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), + Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state * 3))), guiAlpha), + Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_PRESSED)), guiAlpha)); + + // Draw blinking cursor + if (editMode && ((framesCounter / 20) % 2 == 0)) + GuiDrawRectangle( + cursor, 0, BLANK, + Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED)), guiAlpha)); + } else if (state == GUI_STATE_DISABLED) { + GuiDrawRectangle( + bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), + Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state * 3))), guiAlpha), + Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED)), guiAlpha)); + } else + GuiDrawRectangle( + bounds, 1, + Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state * 3))), guiAlpha), + BLANK); + + DrawTextRec( + guiFont, text, textAreaBounds, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), + (float)GuiGetStyle(DEFAULT, TEXT_SPACING), true, + Fade(GetColor(GuiGetStyle(TEXTBOX, TEXT + (state * 3))), guiAlpha)); + //-------------------------------------------------------------------- + + return pressed; +} + +// Slider control with pro parameters +// NOTE: Other GuiSlider*() controls use this one +float GuiSliderPro(Rectangle bounds, const char *textLeft, + const char *textRight, float value, float minValue, + float maxValue, int sliderWidth) { + GuiControlState state = guiState; + + int sliderValue = + (int)(((value - minValue) / (maxValue - minValue)) * + (bounds.width - 2 * GuiGetStyle(SLIDER, BORDER_WIDTH))); + + Rectangle slider = {bounds.x, + bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH) + + GuiGetStyle(SLIDER, SLIDER_PADDING), + 0, + bounds.height - 2 * GuiGetStyle(SLIDER, BORDER_WIDTH) - + 2 * GuiGetStyle(SLIDER, SLIDER_PADDING)}; + + if (sliderWidth > 0) // Slider + { + slider.x += (sliderValue - sliderWidth / 2); + slider.width = (float)sliderWidth; + } else if (sliderWidth == 0) // SliderBar + { + slider.x += GuiGetStyle(SLIDER, BORDER_WIDTH); + slider.width = (float)sliderValue; + } + + // Update control + //-------------------------------------------------------------------- + if ((state != GUI_STATE_DISABLED) && !guiLocked) { + Vector2 mousePoint = GetMousePosition(); + + if (CheckCollisionPointRec(mousePoint, bounds)) { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) { + state = GUI_STATE_PRESSED; + + // Get equivalent value and slider position from mousePoint.x + value = ((maxValue - minValue) * + (mousePoint.x - (float)(bounds.x + sliderWidth / 2))) / + (float)(bounds.width - sliderWidth) + + minValue; + + if (sliderWidth > 0) + slider.x = mousePoint.x - slider.width / 2; // Slider + else if (sliderWidth == 0) + slider.width = (float)sliderValue; // SliderBar + } else + state = GUI_STATE_FOCUSED; + } + + if (value > maxValue) + value = maxValue; + else if (value < minValue) + value = minValue; + } + + // Bar limits check + if (sliderWidth > 0) // Slider + { + if (slider.x <= (bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH))) + slider.x = bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH); + else if ((slider.x + slider.width) >= (bounds.x + bounds.width)) + slider.x = bounds.x + bounds.width - slider.width - + GuiGetStyle(SLIDER, BORDER_WIDTH); + } else if (sliderWidth == 0) // SliderBar + { + if (slider.width > bounds.width) + slider.width = bounds.width - 2 * GuiGetStyle(SLIDER, BORDER_WIDTH); + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle( + bounds, GuiGetStyle(SLIDER, BORDER_WIDTH), + Fade(GetColor(GuiGetStyle(SLIDER, BORDER + (state * 3))), guiAlpha), + Fade(GetColor(GuiGetStyle(SLIDER, (state != GUI_STATE_DISABLED) + ? BASE_COLOR_NORMAL + : BASE_COLOR_DISABLED)), + guiAlpha)); + + // Draw slider internal bar (depends on state) + if ((state == GUI_STATE_NORMAL) || (state == GUI_STATE_PRESSED)) + GuiDrawRectangle( + slider, 0, BLANK, + Fade(GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED)), guiAlpha)); + else if (state == GUI_STATE_FOCUSED) + GuiDrawRectangle( + slider, 0, BLANK, + Fade(GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_FOCUSED)), guiAlpha)); + + // Draw left/right text if provided + if (textLeft != NULL) { + Rectangle textBounds = {0}; + textBounds.width = + (float)GetTextWidth(textLeft); // TODO: Consider text icon + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = + bounds.x - textBounds.width - GuiGetStyle(SLIDER, TEXT_PADDING); + textBounds.y = + bounds.y + bounds.height / 2 - GuiGetStyle(DEFAULT, TEXT_SIZE) / 2; + + GuiDrawText( + textLeft, textBounds, GUI_TEXT_ALIGN_RIGHT, + Fade(GetColor(GuiGetStyle(SLIDER, TEXT + (state * 3))), guiAlpha)); + } + + if (textRight != NULL) { + Rectangle textBounds = {0}; + textBounds.width = + (float)GetTextWidth(textRight); // TODO: Consider text icon + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = bounds.x + bounds.width + GuiGetStyle(SLIDER, TEXT_PADDING); + textBounds.y = + bounds.y + bounds.height / 2 - GuiGetStyle(DEFAULT, TEXT_SIZE) / 2; + + GuiDrawText( + textRight, textBounds, GUI_TEXT_ALIGN_LEFT, + Fade(GetColor(GuiGetStyle(SLIDER, TEXT + (state * 3))), guiAlpha)); + } + //-------------------------------------------------------------------- + + return value; +} + +// Slider control extended, returns selected value and has text +float GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, + float value, float minValue, float maxValue) { + return GuiSliderPro(bounds, textLeft, textRight, value, minValue, maxValue, + GuiGetStyle(SLIDER, SLIDER_WIDTH)); +} + +// Slider Bar control extended, returns selected value +float GuiSliderBar(Rectangle bounds, const char *textLeft, + const char *textRight, float value, float minValue, + float maxValue) { + return GuiSliderPro(bounds, textLeft, textRight, value, minValue, maxValue, + 0); +} + +// Progress Bar control extended, shows current progress value +float GuiProgressBar(Rectangle bounds, const char *textLeft, + const char *textRight, float value, float minValue, + float maxValue) { + GuiControlState state = guiState; + + Rectangle progress = {bounds.x + GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), + bounds.y + GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) + + GuiGetStyle(PROGRESSBAR, PROGRESS_PADDING), + 0, + bounds.height - + 2 * GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - + 2 * GuiGetStyle(PROGRESSBAR, PROGRESS_PADDING)}; + + // Update control + //-------------------------------------------------------------------- + if (state != GUI_STATE_DISABLED) + progress.width = + ((float)(value / (maxValue - minValue)) * + (float)(bounds.width - 2 * GuiGetStyle(PROGRESSBAR, BORDER_WIDTH))); + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle( + bounds, GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), + Fade(GetColor(GuiGetStyle(PROGRESSBAR, BORDER + (state * 3))), guiAlpha), + BLANK); + + // Draw slider internal progress bar (depends on state) + if ((state == GUI_STATE_NORMAL) || (state == GUI_STATE_PRESSED)) + GuiDrawRectangle( + progress, 0, BLANK, + Fade(GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED)), guiAlpha)); + else if (state == GUI_STATE_FOCUSED) + GuiDrawRectangle( + progress, 0, BLANK, + Fade(GetColor(GuiGetStyle(PROGRESSBAR, TEXT_COLOR_FOCUSED)), guiAlpha)); + + // Draw left/right text if provided + if (textLeft != NULL) { + Rectangle textBounds = {0}; + textBounds.width = + (float)GetTextWidth(textLeft); // TODO: Consider text icon + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = + bounds.x - textBounds.width - GuiGetStyle(PROGRESSBAR, TEXT_PADDING); + textBounds.y = + bounds.y + bounds.height / 2 - GuiGetStyle(DEFAULT, TEXT_SIZE) / 2; + + GuiDrawText( + textLeft, textBounds, GUI_TEXT_ALIGN_RIGHT, + Fade(GetColor(GuiGetStyle(PROGRESSBAR, TEXT + (state * 3))), guiAlpha)); + } + + if (textRight != NULL) { + Rectangle textBounds = {0}; + textBounds.width = + (float)GetTextWidth(textRight); // TODO: Consider text icon + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = + bounds.x + bounds.width + GuiGetStyle(PROGRESSBAR, TEXT_PADDING); + textBounds.y = + bounds.y + bounds.height / 2 - GuiGetStyle(DEFAULT, TEXT_SIZE) / 2; + + GuiDrawText( + textRight, textBounds, GUI_TEXT_ALIGN_LEFT, + Fade(GetColor(GuiGetStyle(PROGRESSBAR, TEXT + (state * 3))), guiAlpha)); + } + //-------------------------------------------------------------------- + + return value; +} + +// Status Bar control +void GuiStatusBar(Rectangle bounds, const char *text) { + GuiControlState state = guiState; + + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle( + bounds, GuiGetStyle(STATUSBAR, BORDER_WIDTH), + Fade(GetColor(GuiGetStyle(STATUSBAR, (state != GUI_STATE_DISABLED) + ? BORDER_COLOR_NORMAL + : BORDER_COLOR_DISABLED)), + guiAlpha), + Fade(GetColor(GuiGetStyle(STATUSBAR, (state != GUI_STATE_DISABLED) + ? BASE_COLOR_NORMAL + : BASE_COLOR_DISABLED)), + guiAlpha)); + GuiDrawText(text, GetTextBounds(STATUSBAR, bounds), + GuiGetStyle(STATUSBAR, TEXT_ALIGNMENT), + Fade(GetColor(GuiGetStyle(STATUSBAR, (state != GUI_STATE_DISABLED) + ? TEXT_COLOR_NORMAL + : TEXT_COLOR_DISABLED)), + guiAlpha)); + //-------------------------------------------------------------------- +} + +// Dummy rectangle control, intended for placeholding +void GuiDummyRec(Rectangle bounds, const char *text) { + GuiControlState state = guiState; + + // Update control + //-------------------------------------------------------------------- + if ((state != GUI_STATE_DISABLED) && !guiLocked) { + Vector2 mousePoint = GetMousePosition(); + + // Check button state + if (CheckCollisionPointRec(mousePoint, bounds)) { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + state = GUI_STATE_PRESSED; + else + state = GUI_STATE_FOCUSED; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle( + bounds, 0, BLANK, + Fade(GetColor(GuiGetStyle(DEFAULT, (state != GUI_STATE_DISABLED) + ? BASE_COLOR_NORMAL + : BASE_COLOR_DISABLED)), + guiAlpha)); + GuiDrawText(text, GetTextBounds(DEFAULT, bounds), GUI_TEXT_ALIGN_CENTER, + Fade(GetColor(GuiGetStyle(BUTTON, (state != GUI_STATE_DISABLED) + ? TEXT_COLOR_NORMAL + : TEXT_COLOR_DISABLED)), + guiAlpha)); + //------------------------------------------------------------------ +} + +// Scroll Bar control +// TODO: I feel GuiScrollBar could be simplified... +int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) { + GuiControlState state = guiState; + + // Is the scrollbar horizontal or vertical? + bool isVertical = (bounds.width > bounds.height) ? false : true; + + // The size (width or height depending on scrollbar type) of the spinner + // buttons + const int spinnerSize = + GuiGetStyle(SCROLLBAR, ARROWS_VISIBLE) + ? (isVertical + ? (int)bounds.width - 2 * GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + : (int)bounds.height - + 2 * GuiGetStyle(SCROLLBAR, BORDER_WIDTH)) + : 0; + + // Arrow buttons [<] [>] [∧] [∨] + Rectangle arrowUpLeft = {0}; + Rectangle arrowDownRight = {0}; + + // Actual area of the scrollbar excluding the arrow buttons + Rectangle scrollbar = {0}; + + // Slider bar that moves --[///]----- + Rectangle slider = {0}; + + // Normalize value + if (value > maxValue) + value = maxValue; + if (value < minValue) + value = minValue; + + const int range = maxValue - minValue; + int sliderSize = GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE); + + // Calculate rectangles for all of the components + arrowUpLeft = RAYGUI_CLITERAL(Rectangle){ + (float)bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), + (float)bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), + (float)spinnerSize, (float)spinnerSize}; + + if (isVertical) { + arrowDownRight = RAYGUI_CLITERAL(Rectangle){ + (float)bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), + (float)bounds.y + bounds.height - spinnerSize - + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), + (float)spinnerSize, (float)spinnerSize}; + scrollbar = RAYGUI_CLITERAL(Rectangle){ + bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + + GuiGetStyle(SCROLLBAR, SCROLL_PADDING), + arrowUpLeft.y + arrowUpLeft.height, + bounds.width - 2 * (GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + + GuiGetStyle(SCROLLBAR, SCROLL_PADDING)), + bounds.height - arrowUpLeft.height - arrowDownRight.height - + 2 * GuiGetStyle(SCROLLBAR, BORDER_WIDTH)}; + sliderSize = (sliderSize >= scrollbar.height) + ? ((int)scrollbar.height - 2) + : sliderSize; // Make sure the slider won't get outside of + // the scrollbar + slider = RAYGUI_CLITERAL(Rectangle){ + (float)bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING), + (float)scrollbar.y + (int)(((float)(value - minValue) / range) * + (scrollbar.height - sliderSize)), + (float)bounds.width - + 2 * (GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING)), + (float)sliderSize}; + } else { + arrowDownRight = RAYGUI_CLITERAL(Rectangle){ + (float)bounds.x + bounds.width - spinnerSize - + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), + (float)bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), + (float)spinnerSize, (float)spinnerSize}; + scrollbar = RAYGUI_CLITERAL(Rectangle){ + arrowUpLeft.x + arrowUpLeft.width, + bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + + GuiGetStyle(SCROLLBAR, SCROLL_PADDING), + bounds.width - arrowUpLeft.width - arrowDownRight.width - + 2 * GuiGetStyle(SCROLLBAR, BORDER_WIDTH), + bounds.height - 2 * (GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + + GuiGetStyle(SCROLLBAR, SCROLL_PADDING))}; + sliderSize = (sliderSize >= scrollbar.width) + ? ((int)scrollbar.width - 2) + : sliderSize; // Make sure the slider won't get outside of + // the scrollbar + slider = RAYGUI_CLITERAL(Rectangle){ + (float)scrollbar.x + (int)(((float)(value - minValue) / range) * + (scrollbar.width - sliderSize)), + (float)bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING), + (float)sliderSize, + (float)bounds.height - + 2 * (GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING))}; + } + + // Update control + //-------------------------------------------------------------------- + if ((state != GUI_STATE_DISABLED) && !guiLocked) { + Vector2 mousePoint = GetMousePosition(); + + if (CheckCollisionPointRec(mousePoint, bounds)) { + state = GUI_STATE_FOCUSED; + + // Handle mouse wheel + int wheel = (int)GetMouseWheelMove(); + if (wheel != 0) + value += wheel; + + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { + if (CheckCollisionPointRec(mousePoint, arrowUpLeft)) + value -= range / GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + else if (CheckCollisionPointRec(mousePoint, arrowDownRight)) + value += range / GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + + state = GUI_STATE_PRESSED; + } else if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) { + if (!isVertical) { + Rectangle scrollArea = { + arrowUpLeft.x + arrowUpLeft.width, arrowUpLeft.y, scrollbar.width, + bounds.height - 2 * GuiGetStyle(SCROLLBAR, BORDER_WIDTH)}; + if (CheckCollisionPointRec(mousePoint, scrollArea)) + value = + (int)(((float)(mousePoint.x - scrollArea.x - slider.width / 2) * + range) / + (scrollArea.width - slider.width) + + minValue); + } else { + Rectangle scrollArea = { + arrowUpLeft.x, arrowUpLeft.y + arrowUpLeft.height, + bounds.width - 2 * GuiGetStyle(SCROLLBAR, BORDER_WIDTH), + scrollbar.height}; + if (CheckCollisionPointRec(mousePoint, scrollArea)) + value = (int)(((float)(mousePoint.y - scrollArea.y - + slider.height / 2) * + range) / + (scrollArea.height - slider.height) + + minValue); + } + } + } + + // Normalize value + if (value > maxValue) + value = maxValue; + if (value < minValue) + value = minValue; + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle( + bounds, GuiGetStyle(SCROLLBAR, BORDER_WIDTH), + Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER + state * 3)), guiAlpha), + Fade(GetColor(GuiGetStyle(DEFAULT, BORDER_COLOR_DISABLED)), + guiAlpha)); // Draw the background + + GuiDrawRectangle(scrollbar, 0, BLANK, + Fade(GetColor(GuiGetStyle(BUTTON, BASE_COLOR_NORMAL)), + guiAlpha)); // Draw the scrollbar active area background + GuiDrawRectangle(slider, 0, BLANK, + Fade(GetColor(GuiGetStyle(SLIDER, BORDER + state * 3)), + guiAlpha)); // Draw the slider bar + + // Draw arrows + const int padding = (spinnerSize - GuiGetStyle(SCROLLBAR, ARROWS_SIZE)) / 2; + const Vector2 lineCoords[] = { + // Coordinates for < 0,1,2 + {arrowUpLeft.x + padding, arrowUpLeft.y + spinnerSize / 2}, + {arrowUpLeft.x + spinnerSize - padding, arrowUpLeft.y + padding}, + {arrowUpLeft.x + spinnerSize - padding, + arrowUpLeft.y + spinnerSize - padding}, + + // Coordinates for > 3,4,5 + {arrowDownRight.x + padding, arrowDownRight.y + padding}, + {arrowDownRight.x + spinnerSize - padding, + arrowDownRight.y + spinnerSize / 2}, + {arrowDownRight.x + padding, arrowDownRight.y + spinnerSize - padding}, + + // Coordinates for ∧ 6,7,8 + {arrowUpLeft.x + spinnerSize / 2, arrowUpLeft.y + padding}, + {arrowUpLeft.x + padding, arrowUpLeft.y + spinnerSize - padding}, + {arrowUpLeft.x + spinnerSize - padding, + arrowUpLeft.y + spinnerSize - padding}, + + // Coordinates for ∨ 9,10,11 + {arrowDownRight.x + padding, arrowDownRight.y + padding}, + {arrowDownRight.x + spinnerSize / 2, + arrowDownRight.y + spinnerSize - padding}, + {arrowDownRight.x + spinnerSize - padding, arrowDownRight.y + padding}}; + + Color lineColor = + Fade(GetColor(GuiGetStyle(BUTTON, TEXT + state * 3)), guiAlpha); + + if (GuiGetStyle(SCROLLBAR, ARROWS_VISIBLE)) { + if (isVertical) { + DrawTriangle(lineCoords[6], lineCoords[7], lineCoords[8], lineColor); + DrawTriangle(lineCoords[9], lineCoords[10], lineCoords[11], lineColor); + } else { + DrawTriangle(lineCoords[2], lineCoords[1], lineCoords[0], lineColor); + DrawTriangle(lineCoords[5], lineCoords[4], lineCoords[3], lineColor); + } + } + //-------------------------------------------------------------------- + + return value; +} + +// List View control +int GuiListView(Rectangle bounds, const char *text, int *scrollIndex, + int active) { + int itemsCount = 0; + const char **items = NULL; + + if (text != NULL) + items = GuiTextSplit(text, &itemsCount, NULL); + + return GuiListViewEx(bounds, items, itemsCount, NULL, scrollIndex, active); +} + +// List View control with extended parameters +int GuiListViewEx(Rectangle bounds, const char **text, int count, int *focus, + int *scrollIndex, int active) { + GuiControlState state = guiState; + int itemFocused = (focus == NULL) ? -1 : *focus; + int itemSelected = active; + + // Check if we need a scroll bar + bool useScrollBar = false; + if ((GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + + GuiGetStyle(LISTVIEW, LIST_ITEMS_PADDING)) * + count > + bounds.height) + useScrollBar = true; + + // Define base item rectangle [0] + Rectangle itemBounds = {0}; + itemBounds.x = bounds.x + GuiGetStyle(LISTVIEW, LIST_ITEMS_PADDING); + itemBounds.y = bounds.y + GuiGetStyle(LISTVIEW, LIST_ITEMS_PADDING) + + GuiGetStyle(DEFAULT, BORDER_WIDTH); + itemBounds.width = bounds.width - + 2 * GuiGetStyle(LISTVIEW, LIST_ITEMS_PADDING) - + GuiGetStyle(DEFAULT, BORDER_WIDTH); + itemBounds.height = (float)GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT); + if (useScrollBar) + itemBounds.width -= GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH); + + // Get items on the list + int visibleItems = + (int)bounds.height / (GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + + GuiGetStyle(LISTVIEW, LIST_ITEMS_PADDING)); + if (visibleItems > count) + visibleItems = count; + + int startIndex = (scrollIndex == NULL) ? 0 : *scrollIndex; + if ((startIndex < 0) || (startIndex > (count - visibleItems))) + startIndex = 0; + int endIndex = startIndex + visibleItems; + + // Update control + //-------------------------------------------------------------------- + if ((state != GUI_STATE_DISABLED) && !guiLocked) { + Vector2 mousePoint = GetMousePosition(); + + // Check mouse inside list view + if (CheckCollisionPointRec(mousePoint, bounds)) { + state = GUI_STATE_FOCUSED; + + // Check focused and selected item + for (int i = 0; i < visibleItems; i++) { + if (CheckCollisionPointRec(mousePoint, itemBounds)) { + itemFocused = startIndex + i; + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { + if (itemSelected == (startIndex + i)) + itemSelected = -1; + else + itemSelected = startIndex + i; + } + break; + } + + // Update item rectangle y position for next item + itemBounds.y += (GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + + GuiGetStyle(LISTVIEW, LIST_ITEMS_PADDING)); + } + + if (useScrollBar) { + int wheelMove = (int)GetMouseWheelMove(); + startIndex -= wheelMove; + + if (startIndex < 0) + startIndex = 0; + else if (startIndex > (count - visibleItems)) + startIndex = count - visibleItems; + + endIndex = startIndex + visibleItems; + if (endIndex > count) + endIndex = count; + } + } else + itemFocused = -1; + + // Reset item rectangle y to [0] + itemBounds.y = bounds.y + GuiGetStyle(LISTVIEW, LIST_ITEMS_PADDING) + + GuiGetStyle(DEFAULT, BORDER_WIDTH); + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle( + bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), + Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER + state * 3)), guiAlpha), + GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR))); // Draw background + + // Draw visible items + for (int i = 0; ((i < visibleItems) && (text != NULL)); i++) { + if (state == GUI_STATE_DISABLED) { + if ((startIndex + i) == itemSelected) + GuiDrawRectangle( + itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), + Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_DISABLED)), + guiAlpha), + Fade(GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_DISABLED)), + guiAlpha)); + + GuiDrawText( + text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), + GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), + Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_DISABLED)), guiAlpha)); + } else { + if ((startIndex + i) == itemSelected) { + // Draw item selected + GuiDrawRectangle( + itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), + Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_PRESSED)), + guiAlpha), + Fade(GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_PRESSED)), + guiAlpha)); + GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), + GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), + Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_PRESSED)), + guiAlpha)); + } else if ((startIndex + i) == itemFocused) { + // Draw item focused + GuiDrawRectangle( + itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), + Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_FOCUSED)), + guiAlpha), + Fade(GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_FOCUSED)), + guiAlpha)); + GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), + GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), + Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_FOCUSED)), + guiAlpha)); + } else { + // Draw item normal + GuiDrawText( + text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), + GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), + Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_NORMAL)), guiAlpha)); + } + } + + // Update item rectangle y position for next item + itemBounds.y += (GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + + GuiGetStyle(LISTVIEW, LIST_ITEMS_PADDING)); + } + + if (useScrollBar) { + Rectangle scrollBarBounds = { + bounds.x + bounds.width - GuiGetStyle(LISTVIEW, BORDER_WIDTH) - + GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH), + bounds.y + GuiGetStyle(LISTVIEW, BORDER_WIDTH), + (float)GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH), + bounds.height - 2 * GuiGetStyle(DEFAULT, BORDER_WIDTH)}; + + // Calculate percentage of visible items and apply same percentage to + // scrollbar + float percentVisible = (float)(endIndex - startIndex) / count; + float sliderSize = bounds.height * percentVisible; + + int prevSliderSize = + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE); // Save default slider size + int prevScrollSpeed = + GuiGetStyle(SCROLLBAR, SCROLL_SPEED); // Save default scroll speed + GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, + (int)sliderSize); // Change slider size + GuiSetStyle(SCROLLBAR, SCROLL_SPEED, + count - visibleItems); // Change scroll speed + + startIndex = + GuiScrollBar(scrollBarBounds, startIndex, 0, count - visibleItems); + + GuiSetStyle(SCROLLBAR, SCROLL_SPEED, + prevScrollSpeed); // Reset scroll speed to default + GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, + prevSliderSize); // Reset slider size to default + } + //-------------------------------------------------------------------- + + if (focus != NULL) + *focus = itemFocused; + if (scrollIndex != NULL) + *scrollIndex = startIndex; + + return itemSelected; +} + +// Color Panel control +Color GuiColorPanelEx(Rectangle bounds, Color color, float hue) { + GuiControlState state = guiState; + Vector2 pickerSelector = {0}; + + Vector3 vcolor = {(float)color.r / 255.0f, (float)color.g / 255.0f, + (float)color.b / 255.0f}; + Vector3 hsv = ConvertRGBtoHSV(vcolor); + + pickerSelector.x = bounds.x + (float)hsv.y * bounds.width; // HSV: Saturation + pickerSelector.y = + bounds.y + (1.0f - (float)hsv.z) * bounds.height; // HSV: Value + + Vector3 maxHue = {hue >= 0.0f ? hue : hsv.x, 1.0f, 1.0f}; + Vector3 rgbHue = ConvertHSVtoRGB(maxHue); + Color maxHueCol = {(unsigned char)(255.0f * rgbHue.x), + (unsigned char)(255.0f * rgbHue.y), + (unsigned char)(255.0f * rgbHue.z), 255}; + + const Color colWhite = {255, 255, 255, 255}; + const Color colBlack = {0, 0, 0, 255}; + + // Update control + //-------------------------------------------------------------------- + if ((state != GUI_STATE_DISABLED) && !guiLocked) { + Vector2 mousePoint = GetMousePosition(); + + if (CheckCollisionPointRec(mousePoint, bounds)) { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) { + state = GUI_STATE_PRESSED; + pickerSelector = mousePoint; + + // Calculate color from picker + Vector2 colorPick = {pickerSelector.x - bounds.x, + pickerSelector.y - bounds.y}; + + colorPick.x /= (float)bounds.width; // Get normalized value on x + colorPick.y /= (float)bounds.height; // Get normalized value on y + + hsv.y = colorPick.x; + hsv.z = 1.0f - colorPick.y; + + Vector3 rgb = ConvertHSVtoRGB(hsv); + + // NOTE: Vector3ToColor() only available on raylib 1.8.1 + color = RAYGUI_CLITERAL(Color){ + (unsigned char)(255.0f * rgb.x), (unsigned char)(255.0f * rgb.y), + (unsigned char)(255.0f * rgb.z), + (unsigned char)(255.0f * (float)color.a / 255.0f)}; + + } else + state = GUI_STATE_FOCUSED; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + if (state != GUI_STATE_DISABLED) { + DrawRectangleGradientEx(bounds, Fade(colWhite, guiAlpha), + Fade(colWhite, guiAlpha), Fade(maxHueCol, guiAlpha), + Fade(maxHueCol, guiAlpha)); + DrawRectangleGradientEx(bounds, Fade(colBlack, 0), Fade(colBlack, guiAlpha), + Fade(colBlack, guiAlpha), Fade(colBlack, 0)); + + // Draw color picker: selector + Rectangle selector = { + pickerSelector.x - GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE) / 2, + pickerSelector.y - GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE) / 2, + (float)GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE), + (float)GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE)}; + GuiDrawRectangle(selector, 0, BLANK, Fade(colWhite, guiAlpha)); + } else { + DrawRectangleGradientEx( + bounds, + Fade( + Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), + guiAlpha), + Fade(Fade(colBlack, 0.6f), guiAlpha), + Fade(Fade(colBlack, 0.6f), guiAlpha), + Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), + 0.6f), + guiAlpha)); + } + + GuiDrawRectangle( + bounds, 1, + Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state * 3)), guiAlpha), + BLANK); + //-------------------------------------------------------------------- + + return color; +} + +Color GuiColorPanel(Rectangle bounds, Color color) { + return GuiColorPanelEx(bounds, color, -1.0f); +} + +// Color Bar Alpha control +// NOTE: Returns alpha value normalized [0..1] +float GuiColorBarAlpha(Rectangle bounds, float alpha) { +#define COLORBARALPHA_CHECKED_SIZE 10 + + GuiControlState state = guiState; + Rectangle selector = { + (float)bounds.x + alpha * bounds.width - + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW), + (float)bounds.y - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW), + (float)GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT), + (float)bounds.height + + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW) * 2}; + + // Update control + //-------------------------------------------------------------------- + if ((state != GUI_STATE_DISABLED) && !guiLocked) { + Vector2 mousePoint = GetMousePosition(); + + if (CheckCollisionPointRec(mousePoint, bounds) || + CheckCollisionPointRec(mousePoint, selector)) { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) { + state = GUI_STATE_PRESSED; + selector.x = mousePoint.x - selector.width / 2; + + alpha = (mousePoint.x - bounds.x) / bounds.width; + if (alpha <= 0.0f) + alpha = 0.0f; + if (alpha >= 1.0f) + alpha = 1.0f; + // selector.x = bounds.x + (int)(((alpha - 0)/(100 - 0))*(bounds.width - + // 2*GuiGetStyle(SLIDER, BORDER_WIDTH))) - selector.width/2; + } else + state = GUI_STATE_FOCUSED; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + + // Draw alpha bar: checked background + if (state != GUI_STATE_DISABLED) { + int checksX = (int)bounds.width / COLORBARALPHA_CHECKED_SIZE; + int checksY = (int)bounds.height / COLORBARALPHA_CHECKED_SIZE; + + for (int x = 0; x < checksX; x++) { + for (int y = 0; y < checksY; y++) { + Rectangle check = {bounds.x + x * COLORBARALPHA_CHECKED_SIZE, + bounds.y + y * COLORBARALPHA_CHECKED_SIZE, + COLORBARALPHA_CHECKED_SIZE, + COLORBARALPHA_CHECKED_SIZE}; + GuiDrawRectangle( + check, 0, BLANK, + ((x + y) % 2) ? Fade(Fade(GetColor(GuiGetStyle( + COLORPICKER, BORDER_COLOR_DISABLED)), + 0.4f), + guiAlpha) + : Fade(Fade(GetColor(GuiGetStyle( + COLORPICKER, BASE_COLOR_DISABLED)), + 0.4f), + guiAlpha)); + } + } + + DrawRectangleGradientEx( + bounds, RAYGUI_CLITERAL(Color){255, 255, 255, 0}, + RAYGUI_CLITERAL(Color){255, 255, 255, 0}, + Fade(RAYGUI_CLITERAL(Color){0, 0, 0, 255}, guiAlpha), + Fade(RAYGUI_CLITERAL(Color){0, 0, 0, 255}, guiAlpha)); + } else + DrawRectangleGradientEx( + bounds, + Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), + Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), + Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), + guiAlpha), + Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), + guiAlpha)); + + GuiDrawRectangle( + bounds, 1, + Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state * 3)), guiAlpha), + BLANK); + + // Draw alpha bar: selector + GuiDrawRectangle( + selector, 0, BLANK, + Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state * 3)), guiAlpha)); + //-------------------------------------------------------------------- + + return alpha; +} + +// Color Bar Hue control +// NOTE: Returns hue value normalized [0..1] +float GuiColorBarHue(Rectangle bounds, float hue) { + GuiControlState state = guiState; + Rectangle selector = { + (float)bounds.x - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW), + (float)bounds.y + hue / 360.0f * bounds.height - + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW), + (float)bounds.width + + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW) * 2, + (float)GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT)}; + + // Update control + //-------------------------------------------------------------------- + if ((state != GUI_STATE_DISABLED) && !guiLocked) { + Vector2 mousePoint = GetMousePosition(); + + if (CheckCollisionPointRec(mousePoint, bounds) || + CheckCollisionPointRec(mousePoint, selector)) { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) { + state = GUI_STATE_PRESSED; + selector.y = mousePoint.y - selector.height / 2; + + hue = (mousePoint.y - bounds.y) * 360 / bounds.height; + if (hue <= 0.0f) + hue = 0.0f; + if (hue >= 359.0f) + hue = 359.0f; + + } else + state = GUI_STATE_FOCUSED; + + /*if (IsKeyDown(KEY_UP)) + { + hue -= 2.0f; + if (hue <= 0.0f) hue = 0.0f; + } + else if (IsKeyDown(KEY_DOWN)) + { + hue += 2.0f; + if (hue >= 360.0f) hue = 360.0f; + }*/ + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + if (state != GUI_STATE_DISABLED) { + // Draw hue bar:color bars + DrawRectangleGradientV( + (int)bounds.x + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW) / 2, + (int)bounds.y + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW) / 2, + (int)bounds.width - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW), + (int)bounds.height / 6, + Fade(RAYGUI_CLITERAL(Color){255, 0, 0, 255}, guiAlpha), + Fade(RAYGUI_CLITERAL(Color){255, 255, 0, 255}, guiAlpha)); + DrawRectangleGradientV( + (int)bounds.x + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW) / 2, + (int)bounds.y + (int)bounds.height / 6 + + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW) / 2, + (int)bounds.width - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW), + (int)bounds.height / 6, + Fade(RAYGUI_CLITERAL(Color){255, 255, 0, 255}, guiAlpha), + Fade(RAYGUI_CLITERAL(Color){0, 255, 0, 255}, guiAlpha)); + DrawRectangleGradientV( + (int)bounds.x + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW) / 2, + (int)bounds.y + 2 * ((int)bounds.height / 6) + + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW) / 2, + (int)bounds.width - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW), + (int)bounds.height / 6, + Fade(RAYGUI_CLITERAL(Color){0, 255, 0, 255}, guiAlpha), + Fade(RAYGUI_CLITERAL(Color){0, 255, 255, 255}, guiAlpha)); + DrawRectangleGradientV( + (int)bounds.x + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW) / 2, + (int)bounds.y + 3 * ((int)bounds.height / 6) + + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW) / 2, + (int)bounds.width - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW), + (int)bounds.height / 6, + Fade(RAYGUI_CLITERAL(Color){0, 255, 255, 255}, guiAlpha), + Fade(RAYGUI_CLITERAL(Color){0, 0, 255, 255}, guiAlpha)); + DrawRectangleGradientV( + (int)bounds.x + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW) / 2, + (int)bounds.y + 4 * ((int)bounds.height / 6) + + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW) / 2, + (int)bounds.width - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW), + (int)bounds.height / 6, + Fade(RAYGUI_CLITERAL(Color){0, 0, 255, 255}, guiAlpha), + Fade(RAYGUI_CLITERAL(Color){255, 0, 255, 255}, guiAlpha)); + DrawRectangleGradientV( + (int)bounds.x + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW) / 2, + (int)bounds.y + 5 * ((int)bounds.height / 6) + + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW) / 2, + (int)bounds.width - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW), + (int)bounds.height / 6 - + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW), + Fade(RAYGUI_CLITERAL(Color){255, 0, 255, 255}, guiAlpha), + Fade(RAYGUI_CLITERAL(Color){255, 0, 0, 255}, guiAlpha)); + } else + DrawRectangleGradientV( + (int)bounds.x, (int)bounds.y, (int)bounds.width, (int)bounds.height, + Fade( + Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), + guiAlpha), + Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), + guiAlpha)); + + GuiDrawRectangle( + bounds, 1, + Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state * 3)), guiAlpha), + BLANK); + + // Draw hue bar: selector + GuiDrawRectangle( + selector, 0, BLANK, + Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state * 3)), guiAlpha)); + //-------------------------------------------------------------------- + + return hue; +} + +// TODO: Color GuiColorBarSat() [WHITE->color] +// TODO: Color GuiColorBarValue() [BLACK->color], HSV / HSL +// TODO: float GuiColorBarLuminance() [BLACK->WHITE] + +// Color Picker control +// NOTE: It's divided in multiple controls: +// Color GuiColorPanel(Rectangle bounds, Color color) +// float GuiColorBarAlpha(Rectangle bounds, float alpha) +// float GuiColorBarHue(Rectangle bounds, float value) +// NOTE: bounds define GuiColorPanel() size +Color GuiColorPicker(Rectangle bounds, Color color) { + color = GuiColorPanel(bounds, color); + + Rectangle boundsHue = { + (float)bounds.x + bounds.width + GuiGetStyle(COLORPICKER, HUEBAR_PADDING), + (float)bounds.y, (float)GuiGetStyle(COLORPICKER, HUEBAR_WIDTH), + (float)bounds.height}; + // Rectangle boundsAlpha = { bounds.x, bounds.y + bounds.height + + // GuiGetStyle(COLORPICKER, BARS_PADDING), bounds.width, + // GuiGetStyle(COLORPICKER, BARS_THICK) }; + + Vector3 hsv = ConvertRGBtoHSV(RAYGUI_CLITERAL(Vector3){ + color.r / 255.0f, color.g / 255.0f, color.b / 255.0f}); + hsv.x = GuiColorBarHue(boundsHue, hsv.x); + // color.a = (unsigned char)(GuiColorBarAlpha(boundsAlpha, + // (float)color.a/255.0f)*255.0f); + Vector3 rgb = ConvertHSVtoRGB(hsv); + color = + RAYGUI_CLITERAL(Color){(unsigned char)roundf(rgb.x * 255.0f), + (unsigned char)roundf(rgb.y * 255.0f), + (unsigned char)roundf(rgb.z * 255.0f), color.a}; + + return color; +} + +// Message Box control +int GuiMessageBox(Rectangle bounds, const char *title, const char *message, + const char *buttons) { +#define MESSAGEBOX_BUTTON_HEIGHT 24 +#define MESSAGEBOX_BUTTON_PADDING 10 + + int clicked = -1; // Returns clicked button from buttons list, 0 refers to + // closed window button + + int buttonsCount = 0; + const char **buttonsText = GuiTextSplit(buttons, &buttonsCount, NULL); + Rectangle buttonBounds = {0}; + buttonBounds.x = bounds.x + MESSAGEBOX_BUTTON_PADDING; + buttonBounds.y = bounds.y + bounds.height - MESSAGEBOX_BUTTON_HEIGHT - + MESSAGEBOX_BUTTON_PADDING; + buttonBounds.width = + (bounds.width - MESSAGEBOX_BUTTON_PADDING * (buttonsCount + 1)) / + buttonsCount; + buttonBounds.height = MESSAGEBOX_BUTTON_HEIGHT; + + Vector2 textSize = MeasureTextEx(guiFont, message, + (float)GuiGetStyle(DEFAULT, TEXT_SIZE), 1); + + Rectangle textBounds = {0}; + textBounds.x = bounds.x + bounds.width / 2 - textSize.x / 2; + textBounds.y = bounds.y + WINDOW_STATUSBAR_HEIGHT + + (bounds.height - WINDOW_STATUSBAR_HEIGHT - + MESSAGEBOX_BUTTON_HEIGHT - MESSAGEBOX_BUTTON_PADDING) / + 2 - + textSize.y / 2; + textBounds.width = textSize.x; + textBounds.height = textSize.y; + + // Draw control + //-------------------------------------------------------------------- + if (GuiWindowBox(bounds, title)) + clicked = 0; + + int prevTextAlignment = GuiGetStyle(LABEL, TEXT_ALIGNMENT); + GuiSetStyle(LABEL, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER); + GuiLabel(textBounds, message); + GuiSetStyle(LABEL, TEXT_ALIGNMENT, prevTextAlignment); + + prevTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER); + + for (int i = 0; i < buttonsCount; i++) { + if (GuiButton(buttonBounds, buttonsText[i])) + clicked = i + 1; + buttonBounds.x += (buttonBounds.width + MESSAGEBOX_BUTTON_PADDING); + } + + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, prevTextAlignment); + //-------------------------------------------------------------------- + + return clicked; +} + +// Text Input Box control, ask for text +int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, + const char *buttons, char *text) { +#define TEXTINPUTBOX_BUTTON_HEIGHT 24 +#define TEXTINPUTBOX_BUTTON_PADDING 10 +#define TEXTINPUTBOX_HEIGHT 30 + +#define TEXTINPUTBOX_MAX_TEXT_LENGTH 256 + + // Used to enable text edit mode + // WARNING: No more than one GuiTextInputBox() should be open at the same time + static bool textEditMode = false; + + int btnIndex = -1; + + int buttonsCount = 0; + const char **buttonsText = GuiTextSplit(buttons, &buttonsCount, NULL); + Rectangle buttonBounds = {0}; + buttonBounds.x = bounds.x + TEXTINPUTBOX_BUTTON_PADDING; + buttonBounds.y = bounds.y + bounds.height - TEXTINPUTBOX_BUTTON_HEIGHT - + TEXTINPUTBOX_BUTTON_PADDING; + buttonBounds.width = + (bounds.width - TEXTINPUTBOX_BUTTON_PADDING * (buttonsCount + 1)) / + buttonsCount; + buttonBounds.height = TEXTINPUTBOX_BUTTON_HEIGHT; + + int messageInputHeight = (int)bounds.height - WINDOW_STATUSBAR_HEIGHT - + GuiGetStyle(STATUSBAR, BORDER_WIDTH) - + TEXTINPUTBOX_BUTTON_HEIGHT - + 2 * TEXTINPUTBOX_BUTTON_PADDING; + + Rectangle textBounds = {0}; + if (message != NULL) { + Vector2 textSize = MeasureTextEx(guiFont, message, + (float)GuiGetStyle(DEFAULT, TEXT_SIZE), 1); + + textBounds.x = bounds.x + bounds.width / 2 - textSize.x / 2; + textBounds.y = bounds.y + WINDOW_STATUSBAR_HEIGHT + messageInputHeight / 4 - + textSize.y / 2; + textBounds.width = textSize.x; + textBounds.height = textSize.y; + } + + Rectangle textBoxBounds = {0}; + textBoxBounds.x = bounds.x + TEXTINPUTBOX_BUTTON_PADDING; + textBoxBounds.y = + bounds.y + WINDOW_STATUSBAR_HEIGHT - TEXTINPUTBOX_HEIGHT / 2; + if (message == NULL) + textBoxBounds.y += messageInputHeight / 2; + else + textBoxBounds.y += (messageInputHeight / 2 + messageInputHeight / 4); + textBoxBounds.width = bounds.width - TEXTINPUTBOX_BUTTON_PADDING * 2; + textBoxBounds.height = TEXTINPUTBOX_HEIGHT; + + // Draw control + //-------------------------------------------------------------------- + if (GuiWindowBox(bounds, title)) + btnIndex = 0; + + // Draw message if available + if (message != NULL) { + int prevTextAlignment = GuiGetStyle(LABEL, TEXT_ALIGNMENT); + GuiSetStyle(LABEL, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER); + GuiLabel(textBounds, message); + GuiSetStyle(LABEL, TEXT_ALIGNMENT, prevTextAlignment); + } + + if (GuiTextBox(textBoxBounds, text, TEXTINPUTBOX_MAX_TEXT_LENGTH, + textEditMode)) + textEditMode = !textEditMode; + + int prevBtnTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER); + + for (int i = 0; i < buttonsCount; i++) { + if (GuiButton(buttonBounds, buttonsText[i])) + btnIndex = i + 1; + buttonBounds.x += (buttonBounds.width + MESSAGEBOX_BUTTON_PADDING); + } + + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, prevBtnTextAlignment); + //-------------------------------------------------------------------- + + return btnIndex; +} + +// Grid control +// NOTE: Returns grid mouse-hover selected cell +// About drawing lines at subpixel spacing, simple put, not easy solution: +// https://stackoverflow.com/questions/4435450/2d-opengl-drawing-lines-that-dont-exactly-fit-pixel-raster +Vector2 GuiGrid(Rectangle bounds, float spacing, int subdivs) { +#if !defined(GRID_COLOR_ALPHA) +#define GRID_COLOR_ALPHA 0.15f // Grid lines alpha amount +#endif + + GuiControlState state = guiState; + Vector2 mousePoint = GetMousePosition(); + Vector2 currentCell = {-1, -1}; + + int linesV = ((int)(bounds.width / spacing)) * subdivs + 1; + int linesH = ((int)(bounds.height / spacing)) * subdivs + 1; + + // Update control + //-------------------------------------------------------------------- + if ((state != GUI_STATE_DISABLED) && !guiLocked) { + if (CheckCollisionPointRec(mousePoint, bounds)) { + currentCell.x = (mousePoint.x - bounds.x) / spacing; + currentCell.y = (mousePoint.y - bounds.y) / spacing; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + switch (state) { + case GUI_STATE_NORMAL: { + if (subdivs > 0) { + // Draw vertical grid lines + for (int i = 0; i < linesV; i++) { + Rectangle lineV = {bounds.x + spacing * i / subdivs, bounds.y, 1, + bounds.height}; + GuiDrawRectangle(lineV, 0, BLANK, + ((i % subdivs) == 0) + ? Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), + GRID_COLOR_ALPHA * 4) + : Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), + GRID_COLOR_ALPHA)); + } + + // Draw horizontal grid lines + for (int i = 0; i < linesH; i++) { + Rectangle lineH = {bounds.x, bounds.y + spacing * i / subdivs, + bounds.width, 1}; + GuiDrawRectangle(lineH, 0, BLANK, + ((i % subdivs) == 0) + ? Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), + GRID_COLOR_ALPHA * 4) + : Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), + GRID_COLOR_ALPHA)); + } + } + } break; + default: + break; + } + + return currentCell; +} + +//---------------------------------------------------------------------------------- +// Styles loading functions +//---------------------------------------------------------------------------------- + +// Load raygui style file (.rgs) +void GuiLoadStyle(const char *fileName) { + bool tryBinary = false; + + // Try reading the files as text file first + FILE *rgsFile = fopen(fileName, "rt"); + + if (rgsFile != NULL) { + char buffer[256] = {0}; + fgets(buffer, 256, rgsFile); + + if (buffer[0] == '#') { + int controlId = 0; + int propertyId = 0; + unsigned int propertyValue = 0; + + while (!feof(rgsFile)) { + switch (buffer[0]) { + case 'p': { + // Style property: p + // + + sscanf(buffer, "p %d %d 0x%x", &controlId, &propertyId, + &propertyValue); + + GuiSetStyle(controlId, propertyId, (int)propertyValue); + + } break; + case 'f': { + // Style font: f + + int fontSize = 0; + char charmapFileName[256] = {0}; + char fontFileName[256] = {0}; + sscanf(buffer, "f %d %s %[^\r\n]s", &fontSize, charmapFileName, + fontFileName); + + Font font = {0}; + + if (charmapFileName[0] != '0') { + // Load characters from charmap file, + // expected '\n' separated list of integer values + char *charValues = LoadFileText(charmapFileName); + if (charValues != NULL) { + int charsCount = 0; + const char **chars = TextSplit(charValues, '\n', &charsCount); + + int *values = (int *)RAYGUI_MALLOC(charsCount * sizeof(int)); + for (int i = 0; i < charsCount; i++) + values[i] = TextToInteger(chars[i]); + + font = LoadFontEx( + TextFormat("%s/%s", GetDirectoryPath(fileName), fontFileName), + fontSize, values, charsCount); + + RAYGUI_FREE(values); + } + } else + font = LoadFontEx( + TextFormat("%s/%s", GetDirectoryPath(fileName), fontFileName), + fontSize, NULL, 0); + + if ((font.texture.id > 0) && (font.charsCount > 0)) + GuiSetFont(font); + + } break; + default: + break; + } + + fgets(buffer, 256, rgsFile); + } + } else + tryBinary = true; + + fclose(rgsFile); + } + + if (tryBinary) { + rgsFile = fopen(fileName, "rb"); + + if (rgsFile == NULL) + return; + + char signature[5] = ""; + short version = 0; + short reserved = 0; + int propertiesCount = 0; + + fread(signature, 1, 4, rgsFile); + fread(&version, 1, sizeof(short), rgsFile); + fread(&reserved, 1, sizeof(short), rgsFile); + fread(&propertiesCount, 1, sizeof(int), rgsFile); + + if ((signature[0] == 'r') && (signature[1] == 'G') && + (signature[2] == 'S') && (signature[3] == ' ')) { + short controlId = 0; + short propertyId = 0; + int propertyValue = 0; + + for (int i = 0; i < propertiesCount; i++) { + fread(&controlId, 1, sizeof(short), rgsFile); + fread(&propertyId, 1, sizeof(short), rgsFile); + fread(&propertyValue, 1, sizeof(int), rgsFile); + + if (controlId == 0) // DEFAULT control + { + // If a DEFAULT property is loaded, it is propagated to all controls + // NOTE: All DEFAULT properties should be defined first in the file + GuiSetStyle(0, (int)propertyId, propertyValue); + + if (propertyId < NUM_PROPS_DEFAULT) + for (int i = 1; i < NUM_CONTROLS; i++) + GuiSetStyle(i, (int)propertyId, propertyValue); + } else + GuiSetStyle((int)controlId, (int)propertyId, propertyValue); + } + + // Font loading is highly dependant on raylib API to load font data and + // image + // TODO: Find some mechanism to support it in standalone mode +#if !defined(RAYGUI_STANDALONE) + // Load custom font if available + int fontDataSize = 0; + fread(&fontDataSize, 1, sizeof(int), rgsFile); + + if (fontDataSize > 0) { + Font font = {0}; + int fontType = 0; // 0-Normal, 1-SDF + Rectangle whiteRec = {0}; + + fread(&font.baseSize, 1, sizeof(int), rgsFile); + fread(&font.charsCount, 1, sizeof(int), rgsFile); + fread(&fontType, 1, sizeof(int), rgsFile); + + // Load font white rectangle + fread(&whiteRec, 1, sizeof(Rectangle), rgsFile); + + // Load font image parameters + int fontImageSize = 0; + fread(&fontImageSize, 1, sizeof(int), rgsFile); + + if (fontImageSize > 0) { + Image imFont = {0}; + imFont.mipmaps = 1; + fread(&imFont.width, 1, sizeof(int), rgsFile); + fread(&imFont.height, 1, sizeof(int), rgsFile); + fread(&imFont.format, 1, sizeof(int), rgsFile); + + imFont.data = (unsigned char *)RAYGUI_MALLOC(fontImageSize); + fread(imFont.data, 1, fontImageSize, rgsFile); + + font.texture = LoadTextureFromImage(imFont); + + UnloadImage(imFont); + } + + // Load font recs data + font.recs = + (Rectangle *)RAYGUI_CALLOC(font.charsCount, sizeof(Rectangle)); + for (int i = 0; i < font.charsCount; i++) + fread(&font.recs[i], 1, sizeof(Rectangle), rgsFile); + + // Load font chars info data + font.chars = + (CharInfo *)RAYGUI_CALLOC(font.charsCount, sizeof(CharInfo)); + for (int i = 0; i < font.charsCount; i++) { + fread(&font.chars[i].value, 1, sizeof(int), rgsFile); + fread(&font.chars[i].offsetX, 1, sizeof(int), rgsFile); + fread(&font.chars[i].offsetY, 1, sizeof(int), rgsFile); + fread(&font.chars[i].advanceX, 1, sizeof(int), rgsFile); + } + + GuiSetFont(font); + + // Set font texture source rectangle to be used as white texture to draw + // shapes NOTE: This way, all gui can be draw using a single draw call + if ((whiteRec.width != 0) && (whiteRec.height != 0)) + SetShapesTexture(font.texture, whiteRec); + } +#endif + } + + fclose(rgsFile); + } +} + +// Load style default over global style +void GuiLoadStyleDefault(void) { + // We set this variable first to avoid cyclic function calls + // when calling GuiSetStyle() and GuiGetStyle() + guiStyleLoaded = true; + + // Initialize default LIGHT style property values + GuiSetStyle(DEFAULT, BORDER_COLOR_NORMAL, 0x838383ff); + GuiSetStyle(DEFAULT, BASE_COLOR_NORMAL, 0xc9c9c9ff); + GuiSetStyle(DEFAULT, TEXT_COLOR_NORMAL, 0x686868ff); + GuiSetStyle(DEFAULT, BORDER_COLOR_FOCUSED, 0x5bb2d9ff); + GuiSetStyle(DEFAULT, BASE_COLOR_FOCUSED, 0xc9effeff); + GuiSetStyle(DEFAULT, TEXT_COLOR_FOCUSED, 0x6c9bbcff); + GuiSetStyle(DEFAULT, BORDER_COLOR_PRESSED, 0x0492c7ff); + GuiSetStyle(DEFAULT, BASE_COLOR_PRESSED, 0x97e8ffff); + GuiSetStyle(DEFAULT, TEXT_COLOR_PRESSED, 0x368bafff); + GuiSetStyle(DEFAULT, BORDER_COLOR_DISABLED, 0xb5c1c2ff); + GuiSetStyle(DEFAULT, BASE_COLOR_DISABLED, 0xe6e9e9ff); + GuiSetStyle(DEFAULT, TEXT_COLOR_DISABLED, 0xaeb7b8ff); + GuiSetStyle(DEFAULT, BORDER_WIDTH, + 1); // WARNING: Some controls use other values + GuiSetStyle(DEFAULT, TEXT_PADDING, + 0); // WARNING: Some controls use other values + GuiSetStyle(DEFAULT, TEXT_ALIGNMENT, + GUI_TEXT_ALIGN_CENTER); // WARNING: Some controls use other values + + // Initialize control-specific property values + // NOTE: Those properties are in default list but require specific values by + // control type + GuiSetStyle(LABEL, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_LEFT); + GuiSetStyle(BUTTON, BORDER_WIDTH, 2); + GuiSetStyle(SLIDER, TEXT_PADDING, 5); + GuiSetStyle(CHECKBOX, TEXT_PADDING, 5); + GuiSetStyle(CHECKBOX, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_RIGHT); + GuiSetStyle(TEXTBOX, TEXT_PADDING, 5); + GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_LEFT); + GuiSetStyle(VALUEBOX, TEXT_PADDING, 4); + GuiSetStyle(VALUEBOX, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_LEFT); + GuiSetStyle(SPINNER, TEXT_PADDING, 4); + GuiSetStyle(SPINNER, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_LEFT); + GuiSetStyle(STATUSBAR, TEXT_PADDING, 6); + GuiSetStyle(STATUSBAR, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_LEFT); + + // Initialize extended property values + // NOTE: By default, extended property values are initialized to 0 + GuiSetStyle(DEFAULT, TEXT_SIZE, 10); // DEFAULT, shared by all controls + GuiSetStyle(DEFAULT, TEXT_SPACING, 1); // DEFAULT, shared by all controls + GuiSetStyle(DEFAULT, LINE_COLOR, 0x90abb5ff); // DEFAULT specific property + GuiSetStyle(DEFAULT, BACKGROUND_COLOR, + 0xf5f5f5ff); // DEFAULT specific property + GuiSetStyle(TOGGLE, GROUP_PADDING, 2); + GuiSetStyle(SLIDER, SLIDER_WIDTH, 15); + GuiSetStyle(SLIDER, SLIDER_PADDING, 1); + GuiSetStyle(PROGRESSBAR, PROGRESS_PADDING, 1); + GuiSetStyle(CHECKBOX, CHECK_PADDING, 1); + GuiSetStyle(COMBOBOX, COMBO_BUTTON_WIDTH, 30); + GuiSetStyle(COMBOBOX, COMBO_BUTTON_PADDING, 2); + GuiSetStyle(DROPDOWNBOX, ARROW_PADDING, 16); + GuiSetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_PADDING, 2); + GuiSetStyle(TEXTBOX, TEXT_LINES_PADDING, 5); + GuiSetStyle(TEXTBOX, TEXT_INNER_PADDING, 4); + GuiSetStyle(TEXTBOX, COLOR_SELECTED_FG, 0xf0fffeff); + GuiSetStyle(TEXTBOX, COLOR_SELECTED_BG, 0x839affe0); + GuiSetStyle(SPINNER, SPIN_BUTTON_WIDTH, 20); + GuiSetStyle(SPINNER, SPIN_BUTTON_PADDING, 2); + GuiSetStyle(SCROLLBAR, BORDER_WIDTH, 0); + GuiSetStyle(SCROLLBAR, ARROWS_VISIBLE, 0); + GuiSetStyle(SCROLLBAR, ARROWS_SIZE, 6); + GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING, 0); + GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, 16); + GuiSetStyle(SCROLLBAR, SCROLL_PADDING, 0); + GuiSetStyle(SCROLLBAR, SCROLL_SPEED, 10); + GuiSetStyle(LISTVIEW, LIST_ITEMS_HEIGHT, 0x1e); + GuiSetStyle(LISTVIEW, LIST_ITEMS_PADDING, 2); + GuiSetStyle(LISTVIEW, SCROLLBAR_WIDTH, 10); + GuiSetStyle(LISTVIEW, SCROLLBAR_SIDE, SCROLLBAR_RIGHT_SIDE); + GuiSetStyle(COLORPICKER, COLOR_SELECTOR_SIZE, 6); + GuiSetStyle(COLORPICKER, HUEBAR_WIDTH, 0x14); + GuiSetStyle(COLORPICKER, HUEBAR_PADDING, 0xa); + GuiSetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT, 6); + GuiSetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW, 2); + + guiFont = GetFontDefault(); // Initialize default font +} + +// Get text with icon id prepended +// NOTE: Useful to add icons by name id (enum) instead of +// a number that can change between ricon versions +const char *GuiIconText(int iconId, const char *text) { +#if defined(RAYGUI_SUPPORT_ICONS) + static char buffer[1024] = {0}; + memset(buffer, 0, 1024); + + sprintf(buffer, "#%03i#", iconId); + + if (text != NULL) { + for (int i = 5; i < 1024; i++) { + buffer[i] = text[i - 5]; + if (text[i - 5] == '\0') + break; + } + } + + return buffer; +#else + return NULL; +#endif +} + +#if defined(RAYGUI_SUPPORT_ICONS) + +// Get full icons data pointer +unsigned int *GuiGetIcons(void) { return guiIcons; } + +// Load raygui icons file (.rgi) +// NOTE: In case nameIds are required, they can be requested with loadIconsName, +// they are returned as a guiIconsName[iconsCount][RICON_MAX_NAME_LENGTH], +// guiIconsName[]][] memory should be manually freed! +char **GuiLoadIcons(const char *fileName, bool loadIconsName) { + // Style File Structure (.rgi) + // ------------------------------------------------------ + // Offset | Size | Type | Description + // ------------------------------------------------------ + // 0 | 4 | char | Signature: "rGI " + // 4 | 2 | short | Version: 100 + // 6 | 2 | short | reserved + + // 8 | 2 | short | Num icons (N) + // 10 | 2 | short | Icons size (Options: 16, 32, 64) (S) + + // Icons name id (32 bytes per name id) + // foreach (icon) + // { + // 12+32*i | 32 | char | Icon NameId + // } + + // Icons data: One bit per pixel, stored as unsigned int array (depends on + // icon size) S*S pixels/32bit per unsigned int = K unsigned int per icon + // foreach (icon) + // { + // ... | K | unsigned int | Icon Data + // } + + FILE *rgiFile = fopen(fileName, "rb"); + + char **guiIconsName = NULL; + + if (rgiFile != NULL) { + char signature[5] = ""; + short version = 0; + short reserved = 0; + short iconsCount = 0; + short iconsSize = 0; + + fread(signature, 1, 4, rgiFile); + fread(&version, 1, sizeof(short), rgiFile); + fread(&reserved, 1, sizeof(short), rgiFile); + fread(&iconsCount, 1, sizeof(short), rgiFile); + fread(&iconsSize, 1, sizeof(short), rgiFile); + + if ((signature[0] == 'r') && (signature[1] == 'G') && + (signature[2] == 'I') && (signature[3] == ' ')) { + if (loadIconsName) { + guiIconsName = (char **)RAYGUI_MALLOC(iconsCount * sizeof(char **)); + for (int i = 0; i < iconsCount; i++) { + guiIconsName[i] = (char *)RAYGUI_MALLOC(RICON_MAX_NAME_LENGTH); + fread(guiIconsName[i], 32, 1, rgiFile); + } + } + + // Read icons data directly over guiIcons data array + fread(guiIcons, iconsCount * (iconsSize * iconsSize / 32), + sizeof(unsigned int), rgiFile); + } + + fclose(rgiFile); + } + + return guiIconsName; +} + +// Draw selected icon using rectangles pixel-by-pixel +void GuiDrawIcon(int iconId, Vector2 position, int pixelSize, Color color) { +#define BIT_CHECK(a, b) ((a) & (1 << (b))) + + for (int i = 0, y = 0; i < RICON_SIZE * RICON_SIZE / 32; i++) { + for (int k = 0; k < 32; k++) { + if (BIT_CHECK(guiIcons[iconId * RICON_DATA_ELEMENTS + i], k)) { +#if !defined(RAYGUI_STANDALONE) + DrawRectangle(position.x + (k % RICON_SIZE) * pixelSize, + position.y + y * pixelSize, pixelSize, pixelSize, color); +#endif + } + + if ((k == 15) || (k == 31)) + y++; + } + } +} + +// Get icon bit data +// NOTE: Bit data array grouped as unsigned int (ICON_SIZE*ICON_SIZE/32 +// elements) +unsigned int *GuiGetIconData(int iconId) { + static unsigned int iconData[RICON_DATA_ELEMENTS] = {0}; + memset(iconData, 0, RICON_DATA_ELEMENTS * sizeof(unsigned int)); + + if (iconId < RICON_MAX_ICONS) + memcpy(iconData, &guiIcons[iconId * RICON_DATA_ELEMENTS], + RICON_DATA_ELEMENTS * sizeof(unsigned int)); + + return iconData; +} + +// Set icon bit data +// NOTE: Data must be provided as unsigned int array (ICON_SIZE*ICON_SIZE/32 +// elements) +void GuiSetIconData(int iconId, unsigned int *data) { + if (iconId < RICON_MAX_ICONS) + memcpy(&guiIcons[iconId * RICON_DATA_ELEMENTS], data, + RICON_DATA_ELEMENTS * sizeof(unsigned int)); +} + +// Set icon pixel value +void GuiSetIconPixel(int iconId, int x, int y) { +#define BIT_SET(a, b) ((a) |= (1 << (b))) + + // This logic works for any RICON_SIZE pixels icons, + // For example, in case of 16x16 pixels, every 2 lines fit in one unsigned int + // data element + BIT_SET(guiIcons[iconId * RICON_DATA_ELEMENTS + + y / (sizeof(unsigned int) * 8 / RICON_SIZE)], + x + (y % (sizeof(unsigned int) * 8 / RICON_SIZE) * RICON_SIZE)); +} + +// Clear icon pixel value +void GuiClearIconPixel(int iconId, int x, int y) { +#define BIT_CLEAR(a, b) ((a) &= ~((1) << (b))) + + // This logic works for any RICON_SIZE pixels icons, + // For example, in case of 16x16 pixels, every 2 lines fit in one unsigned int + // data element + BIT_CLEAR(guiIcons[iconId * RICON_DATA_ELEMENTS + + y / (sizeof(unsigned int) * 8 / RICON_SIZE)], + x + (y % (sizeof(unsigned int) * 8 / RICON_SIZE) * RICON_SIZE)); +} + +// Check icon pixel value +bool GuiCheckIconPixel(int iconId, int x, int y) { +#define BIT_CHECK(a, b) ((a) & (1 << (b))) + + return (BIT_CHECK(guiIcons[iconId * 8 + y / 2], x + (y % 2 * 16))); +} +#endif // RAYGUI_SUPPORT_ICONS + +//---------------------------------------------------------------------------------- +// Module specific Functions Definition +//---------------------------------------------------------------------------------- +// Gui get text width using default font +static int GetTextWidth(const char *text) { + Vector2 size = {0}; + + if ((text != NULL) && (text[0] != '\0')) + size = MeasureTextEx(guiFont, text, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); + + // TODO: Consider text icon width here??? + + return (int)size.x; +} + +// Get text bounds considering control bounds +static Rectangle GetTextBounds(int control, Rectangle bounds) { + Rectangle textBounds = bounds; + + textBounds.x = bounds.x + GuiGetStyle(control, BORDER_WIDTH); + textBounds.y = bounds.y + GuiGetStyle(control, BORDER_WIDTH); + textBounds.width = bounds.width - 2 * GuiGetStyle(control, BORDER_WIDTH); + textBounds.height = bounds.height - 2 * GuiGetStyle(control, BORDER_WIDTH); + + // Consider TEXT_PADDING properly, depends on control type and TEXT_ALIGNMENT + switch (control) { + case COMBOBOX: + bounds.width -= (GuiGetStyle(control, COMBO_BUTTON_WIDTH) + + GuiGetStyle(control, COMBO_BUTTON_PADDING)); + break; + case VALUEBOX: + break; // NOTE: ValueBox text value always centered, text padding applies to + // label + default: { + if (GuiGetStyle(control, TEXT_ALIGNMENT) == GUI_TEXT_ALIGN_RIGHT) + textBounds.x -= GuiGetStyle(control, TEXT_PADDING); + else + textBounds.x += GuiGetStyle(control, TEXT_PADDING); + } break; + } + + // TODO: Special cases (no label): COMBOBOX, DROPDOWNBOX, LISTVIEW + // (scrollbar?) More special cases (label side): CHECKBOX, SLIDER, VALUEBOX, + // SPINNER + + return textBounds; +} + +// Get text icon if provided and move text cursor +// NOTE: We support up to 999 values for iconId +static const char *GetTextIcon(const char *text, int *iconId) { +#if defined(RAYGUI_SUPPORT_ICONS) + *iconId = -1; + if (text[0] == '#') // Maybe we have an icon! + { + char iconValue[4] = {0}; // Maximum length for icon value: 3 digits + '\0' + + int pos = 1; + while ((pos < 4) && (text[pos] >= '0') && (text[pos] <= '9')) { + iconValue[pos - 1] = text[pos]; + pos++; + } + + if (text[pos] == '#') { + *iconId = TextToInteger(iconValue); + + // Move text pointer after icon + // WARNING: If only icon provided, it could point to EOL character! + if (*iconId >= 0) + text += (pos + 1); + } + } +#endif + + return text; +} + +// Gui draw text using default font +static void GuiDrawText(const char *text, Rectangle bounds, int alignment, + Color tint) { +#define TEXT_VALIGN_PIXEL_OFFSET(h) \ + ((int)h % 2) // Vertical alignment for pixel perfect + + if ((text != NULL) && (text[0] != '\0')) { + int iconId = 0; + text = GetTextIcon(text, &iconId); // Check text for icon and move cursor + +// Get text position depending on alignment and iconId +//--------------------------------------------------------------------------------- +#define ICON_TEXT_PADDING 4 + + Vector2 position = {bounds.x, bounds.y}; + + // NOTE: We get text size after icon been processed + int textWidth = GetTextWidth(text); + int textHeight = GuiGetStyle(DEFAULT, TEXT_SIZE); + +#if defined(RAYGUI_SUPPORT_ICONS) + if (iconId >= 0) { + textWidth += RICON_SIZE; + + // WARNING: If only icon provided, text could be pointing to eof + // character! + if ((text != NULL) && (text[0] != '\0')) + textWidth += ICON_TEXT_PADDING; + } +#endif + // Check guiTextAlign global variables + switch (alignment) { + case GUI_TEXT_ALIGN_LEFT: { + position.x = bounds.x; + position.y = bounds.y + bounds.height / 2 - textHeight / 2 + + TEXT_VALIGN_PIXEL_OFFSET(bounds.height); + } break; + case GUI_TEXT_ALIGN_CENTER: { + position.x = bounds.x + bounds.width / 2 - textWidth / 2; + position.y = bounds.y + bounds.height / 2 - textHeight / 2 + + TEXT_VALIGN_PIXEL_OFFSET(bounds.height); + } break; + case GUI_TEXT_ALIGN_RIGHT: { + position.x = bounds.x + bounds.width - textWidth; + position.y = bounds.y + bounds.height / 2 - textHeight / 2 + + TEXT_VALIGN_PIXEL_OFFSET(bounds.height); + } break; + default: + break; + } + + // NOTE: Make sure we get pixel-perfect coordinates, + // In case of decimals we got weird text positioning + position.x = (float)((int)position.x); + position.y = (float)((int)position.y); + //--------------------------------------------------------------------------------- + + // Draw text (with icon if available) + //--------------------------------------------------------------------------------- +#if defined(RAYGUI_SUPPORT_ICONS) + if (iconId >= 0) { + // NOTE: We consider icon height, probably different than text size + GuiDrawIcon(iconId, + RAYGUI_CLITERAL(Vector2){ + position.x, bounds.y + bounds.height / 2 - + RICON_SIZE / 2 + + TEXT_VALIGN_PIXEL_OFFSET(bounds.height)}, + 1, tint); + position.x += (RICON_SIZE + ICON_TEXT_PADDING); + } +#endif + DrawTextEx(guiFont, text, position, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), + (float)GuiGetStyle(DEFAULT, TEXT_SPACING), tint); + //--------------------------------------------------------------------------------- + } +} + +// Gui draw rectangle using default raygui plain style with borders +static void GuiDrawRectangle(Rectangle rec, int borderWidth, Color borderColor, + Color color) { + if (color.a > 0) { + // Draw rectangle filled with color + DrawRectangle((int)rec.x, (int)rec.y, (int)rec.width, (int)rec.height, + color); + } + + if (borderWidth > 0) { + // Draw rectangle border lines with color + DrawRectangle((int)rec.x, (int)rec.y, (int)rec.width, borderWidth, + borderColor); + DrawRectangle((int)rec.x, (int)rec.y + borderWidth, borderWidth, + (int)rec.height - 2 * borderWidth, borderColor); + DrawRectangle((int)rec.x + (int)rec.width - borderWidth, + (int)rec.y + borderWidth, borderWidth, + (int)rec.height - 2 * borderWidth, borderColor); + DrawRectangle((int)rec.x, (int)rec.y + (int)rec.height - borderWidth, + (int)rec.width, borderWidth, borderColor); + } + + // TODO: For n-patch-based style we would need: [state] and maybe [control] + // In this case all controls drawing logic should be moved to this function... + // I don't like it... +} + +// Split controls text into multiple strings +// Also check for multiple columns (required by GuiToggleGroup()) +static const char **GuiTextSplit(const char *text, int *count, int *textRow) { + // NOTE: Current implementation returns a copy of the provided string with + // '\0' (string end delimiter) inserted between strings defined by "delimiter" + // parameter. No memory is dynamically allocated, all used memory is static... + // it has some limitations: + // 1. Maximum number of possible split strings is set by + // TEXTSPLIT_MAX_TEXT_ELEMENTS + // 2. Maximum size of text to split is TEXTSPLIT_MAX_TEXT_LENGTH + // NOTE: Those definitions could be externally provided if required + +#if !defined(TEXTSPLIT_MAX_TEXT_LENGTH) +#define TEXTSPLIT_MAX_TEXT_LENGTH 1024 +#endif + +#if !defined(TEXTSPLIT_MAX_TEXT_ELEMENTS) +#define TEXTSPLIT_MAX_TEXT_ELEMENTS 128 +#endif + + static const char *result[TEXTSPLIT_MAX_TEXT_ELEMENTS] = {NULL}; + static char buffer[TEXTSPLIT_MAX_TEXT_LENGTH] = {0}; + memset(buffer, 0, TEXTSPLIT_MAX_TEXT_LENGTH); + + result[0] = buffer; + int counter = 1; + + if (textRow != NULL) + textRow[0] = 0; + + // Count how many substrings we have on text and point to every one + for (int i = 0; i < TEXTSPLIT_MAX_TEXT_LENGTH; i++) { + buffer[i] = text[i]; + if (buffer[i] == '\0') + break; + else if ((buffer[i] == ';') || (buffer[i] == '\n')) { + result[counter] = buffer + i + 1; + + if (textRow != NULL) { + if (buffer[i] == '\n') + textRow[counter] = textRow[counter - 1] + 1; + else + textRow[counter] = textRow[counter - 1]; + } + + buffer[i] = '\0'; // Set an end of string at this point + + counter++; + if (counter == TEXTSPLIT_MAX_TEXT_ELEMENTS) + break; + } + } + + *count = counter; + + return result; +} + +// Convert color data from RGB to HSV +// NOTE: Color data should be passed normalized +static Vector3 ConvertRGBtoHSV(Vector3 rgb) { + Vector3 hsv = {0}; + float min = 0.0f; + float max = 0.0f; + float delta = 0.0f; + + min = (rgb.x < rgb.y) ? rgb.x : rgb.y; + min = (min < rgb.z) ? min : rgb.z; + + max = (rgb.x > rgb.y) ? rgb.x : rgb.y; + max = (max > rgb.z) ? max : rgb.z; + + hsv.z = max; // Value + delta = max - min; + + if (delta < 0.00001f) { + hsv.y = 0.0f; + hsv.x = 0.0f; // Undefined, maybe NAN? + return hsv; + } + + if (max > 0.0f) { + // NOTE: If max is 0, this divide would cause a crash + hsv.y = (delta / max); // Saturation + } else { + // NOTE: If max is 0, then r = g = b = 0, s = 0, h is undefined + hsv.y = 0.0f; + hsv.x = 0.0f; // Undefined, maybe NAN? + return hsv; + } + + // NOTE: Comparing float values could not work properly + if (rgb.x >= max) + hsv.x = (rgb.y - rgb.z) / delta; // Between yellow & magenta + else { + if (rgb.y >= max) + hsv.x = 2.0f + (rgb.z - rgb.x) / delta; // Between cyan & yellow + else + hsv.x = 4.0f + (rgb.x - rgb.y) / delta; // Between magenta & cyan + } + + hsv.x *= 60.0f; // Convert to degrees + + if (hsv.x < 0.0f) + hsv.x += 360.0f; + + return hsv; +} + +// Convert color data from HSV to RGB +// NOTE: Color data should be passed normalized +static Vector3 ConvertHSVtoRGB(Vector3 hsv) { + Vector3 rgb = {0}; + float hh = 0.0f, p = 0.0f, q = 0.0f, t = 0.0f, ff = 0.0f; + long i = 0; + + // NOTE: Comparing float values could not work properly + if (hsv.y <= 0.0f) { + rgb.x = hsv.z; + rgb.y = hsv.z; + rgb.z = hsv.z; + return rgb; + } + + hh = hsv.x; + if (hh >= 360.0f) + hh = 0.0f; + hh /= 60.0f; + + i = (long)hh; + ff = hh - i; + p = hsv.z * (1.0f - hsv.y); + q = hsv.z * (1.0f - (hsv.y * ff)); + t = hsv.z * (1.0f - (hsv.y * (1.0f - ff))); + + switch (i) { + case 0: { + rgb.x = hsv.z; + rgb.y = t; + rgb.z = p; + } break; + case 1: { + rgb.x = q; + rgb.y = hsv.z; + rgb.z = p; + } break; + case 2: { + rgb.x = p; + rgb.y = hsv.z; + rgb.z = t; + } break; + case 3: { + rgb.x = p; + rgb.y = q; + rgb.z = hsv.z; + } break; + case 4: { + rgb.x = t; + rgb.y = p; + rgb.z = hsv.z; + } break; + case 5: + default: { + rgb.x = hsv.z; + rgb.y = p; + rgb.z = q; + } break; + } + + return rgb; +} + +#if defined(RAYGUI_STANDALONE) +// Returns a Color struct from hexadecimal value +static Color GetColor(int hexValue) { + Color color; + + color.r = (unsigned char)(hexValue >> 24) & 0xFF; + color.g = (unsigned char)(hexValue >> 16) & 0xFF; + color.b = (unsigned char)(hexValue >> 8) & 0xFF; + color.a = (unsigned char)hexValue & 0xFF; + + return color; +} + +// Returns hexadecimal value for a Color +static int ColorToInt(Color color) { + return (((int)color.r << 24) | ((int)color.g << 16) | ((int)color.b << 8) | + (int)color.a); +} + +// Check if point is inside rectangle +static bool CheckCollisionPointRec(Vector2 point, Rectangle rec) { + bool collision = false; + + if ((point.x >= rec.x) && (point.x <= (rec.x + rec.width)) && + (point.y >= rec.y) && (point.y <= (rec.y + rec.height))) + collision = true; + + return collision; +} + +// Color fade-in or fade-out, alpha goes from 0.0f to 1.0f +static Color Fade(Color color, float alpha) { + if (alpha < 0.0f) + alpha = 0.0f; + else if (alpha > 1.0f) + alpha = 1.0f; + + Color result = {color.r, color.g, color.b, (unsigned char)(255.0f * alpha)}; + + return result; +} + +// Formatting of text with variables to 'embed' +static const char *TextFormat(const char *text, ...) { +#define MAX_FORMATTEXT_LENGTH 64 + + static char buffer[MAX_FORMATTEXT_LENGTH]; + + va_list args; + va_start(args, text); + vsprintf(buffer, text, args); + va_end(args); + + return buffer; +} + +// Draw rectangle with vertical gradient fill color +// NOTE: This function is only used by GuiColorPicker() +static void DrawRectangleGradientV(int posX, int posY, int width, int height, + Color color1, Color color2) { + Rectangle bounds = {(float)posX, (float)posY, (float)width, (float)height}; + DrawRectangleGradientEx(bounds, color1, color2, color2, color1); +} + +#define TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH \ + 1024 // Size of static buffer: TextSplit() +#define TEXTSPLIT_MAX_SUBSTRINGS_COUNT \ + 128 // Size of static pointers array: TextSplit() + +// Split string into multiple strings +const char **TextSplit(const char *text, char delimiter, int *count) { + // NOTE: Current implementation returns a copy of the provided string with + // '\0' (string end delimiter) inserted between strings defined by "delimiter" + // parameter. No memory is dynamically allocated, all used memory is static... + // it has some limitations: + // 1. Maximum number of possible split strings is set by + // TEXTSPLIT_MAX_SUBSTRINGS_COUNT + // 2. Maximum size of text to split is TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH + + static const char *result[TEXTSPLIT_MAX_SUBSTRINGS_COUNT] = {NULL}; + static char buffer[TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH] = {0}; + memset(buffer, 0, TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH); + + result[0] = buffer; + int counter = 0; + + if (text != NULL) { + counter = 1; + + // Count how many substrings we have on text and point to every one + for (int i = 0; i < TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH; i++) { + buffer[i] = text[i]; + if (buffer[i] == '\0') + break; + else if (buffer[i] == delimiter) { + buffer[i] = '\0'; // Set an end of string at this point + result[counter] = buffer + i + 1; + counter++; + + if (counter == TEXTSPLIT_MAX_SUBSTRINGS_COUNT) + break; + } + } + } + + *count = counter; + return result; +} + +// Get integer value from text +// NOTE: This function replaces atoi() [stdlib.h] +static int TextToInteger(const char *text) { + int value = 0; + int sign = 1; + + if ((text[0] == '+') || (text[0] == '-')) { + if (text[0] == '-') + sign = -1; + text++; + } + + for (int i = 0; ((text[i] >= '0') && (text[i] <= '9')); ++i) + value = value * 10 + (int)(text[i] - '0'); + + return value * sign; +} + +// Encode codepoint into utf8 text (char array length returned as parameter) +static const char *CodepointToUtf8(int codepoint, int *byteLength) { + static char utf8[6] = {0}; + int length = 0; + + if (codepoint <= 0x7f) { + utf8[0] = (char)codepoint; + length = 1; + } else if (codepoint <= 0x7ff) { + utf8[0] = (char)(((codepoint >> 6) & 0x1f) | 0xc0); + utf8[1] = (char)((codepoint & 0x3f) | 0x80); + length = 2; + } else if (codepoint <= 0xffff) { + utf8[0] = (char)(((codepoint >> 12) & 0x0f) | 0xe0); + utf8[1] = (char)(((codepoint >> 6) & 0x3f) | 0x80); + utf8[2] = (char)((codepoint & 0x3f) | 0x80); + length = 3; + } else if (codepoint <= 0x10ffff) { + utf8[0] = (char)(((codepoint >> 18) & 0x07) | 0xf0); + utf8[1] = (char)(((codepoint >> 12) & 0x3f) | 0x80); + utf8[2] = (char)(((codepoint >> 6) & 0x3f) | 0x80); + utf8[3] = (char)((codepoint & 0x3f) | 0x80); + length = 4; + } + + *byteLength = length; + + return utf8; +} +#endif // RAYGUI_STANDALONE + +#endif // RAYGUI_IMPLEMENTATION diff --git a/src/ricons.h b/src/ricons.h new file mode 100644 index 000000000..9fa5f7b8a --- /dev/null +++ b/src/ricons.h @@ -0,0 +1,812 @@ +/********************************************************************************************** + * + * rIcons - Icons pack intended for tools development with raygui + * + * LICENSE: zlib/libpng + * + * Copyright (c) 2019-2020 Ramon Santamaria (@raysan5) + * + **********************************************************************************************/ + +#ifndef RICONS_H +#define RICONS_H + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +#define RICON_MAX_ICONS 256 // Maximum number of icons +#define RICON_SIZE 16 // Size of icons (squared) + +#define RICON_MAX_NAME_LENGTH 32 // Maximum length of icon name id + +// Icons data is defined by bit array (every bit represents one pixel) +// Those arrays are stored as unsigned int data arrays, so every array +// element defines 32 pixels (bits) of information +// Number of elemens depend on RICON_SIZE (by default 16x16 pixels) +#define RICON_DATA_ELEMENTS (RICON_SIZE * RICON_SIZE / 32) + +//---------------------------------------------------------------------------------- +// Icons enumeration +//---------------------------------------------------------------------------------- +typedef enum { + RICON_NONE = 0, + RICON_FOLDER_FILE_OPEN = 1, + RICON_FILE_SAVE_CLASSIC = 2, + RICON_FOLDER_OPEN = 3, + RICON_FOLDER_SAVE = 4, + RICON_FILE_OPEN = 5, + RICON_FILE_SAVE = 6, + RICON_FILE_EXPORT = 7, + RICON_FILE_NEW = 8, + RICON_FILE_DELETE = 9, + RICON_FILETYPE_TEXT = 10, + RICON_FILETYPE_AUDIO = 11, + RICON_FILETYPE_IMAGE = 12, + RICON_FILETYPE_PLAY = 13, + RICON_FILETYPE_VIDEO = 14, + RICON_FILETYPE_INFO = 15, + RICON_FILE_COPY = 16, + RICON_FILE_CUT = 17, + RICON_FILE_PASTE = 18, + RICON_CURSOR_HAND = 19, + RICON_CURSOR_POINTER = 20, + RICON_CURSOR_CLASSIC = 21, + RICON_PENCIL = 22, + RICON_PENCIL_BIG = 23, + RICON_BRUSH_CLASSIC = 24, + RICON_BRUSH_PAINTER = 25, + RICON_WATER_DROP = 26, + RICON_COLOR_PICKER = 27, + RICON_RUBBER = 28, + RICON_COLOR_BUCKET = 29, + RICON_TEXT_T = 30, + RICON_TEXT_A = 31, + RICON_SCALE = 32, + RICON_RESIZE = 33, + RICON_FILTER_POINT = 34, + RICON_FILTER_BILINEAR = 35, + RICON_CROP = 36, + RICON_CROP_ALPHA = 37, + RICON_SQUARE_TOGGLE = 38, + RICON_SYMMETRY = 39, + RICON_SYMMETRY_HORIZONTAL = 40, + RICON_SYMMETRY_VERTICAL = 41, + RICON_LENS = 42, + RICON_LENS_BIG = 43, + RICON_EYE_ON = 44, + RICON_EYE_OFF = 45, + RICON_FILTER_TOP = 46, + RICON_FILTER = 47, + RICON_TARGET_POINT = 48, + RICON_TARGET_SMALL = 49, + RICON_TARGET_BIG = 50, + RICON_TARGET_MOVE = 51, + RICON_CURSOR_MOVE = 52, + RICON_CURSOR_SCALE = 53, + RICON_CURSOR_SCALE_RIGHT = 54, + RICON_CURSOR_SCALE_LEFT = 55, + RICON_UNDO = 56, + RICON_REDO = 57, + RICON_REREDO = 58, + RICON_MUTATE = 59, + RICON_ROTATE = 60, + RICON_REPEAT = 61, + RICON_SHUFFLE = 62, + RICON_EMPTYBOX = 63, + RICON_TARGET = 64, + RICON_TARGET_SMALL_FILL = 65, + RICON_TARGET_BIG_FILL = 66, + RICON_TARGET_MOVE_FILL = 67, + RICON_CURSOR_MOVE_FILL = 68, + RICON_CURSOR_SCALE_FILL = 69, + RICON_CURSOR_SCALE_RIGHT_FILL = 70, + RICON_CURSOR_SCALE_LEFT_FILL = 71, + RICON_UNDO_FILL = 72, + RICON_REDO_FILL = 73, + RICON_REREDO_FILL = 74, + RICON_MUTATE_FILL = 75, + RICON_ROTATE_FILL = 76, + RICON_REPEAT_FILL = 77, + RICON_SHUFFLE_FILL = 78, + RICON_EMPTYBOX_SMALL = 79, + RICON_BOX = 80, + RICON_BOX_TOP = 81, + RICON_BOX_TOP_RIGHT = 82, + RICON_BOX_RIGHT = 83, + RICON_BOX_BOTTOM_RIGHT = 84, + RICON_BOX_BOTTOM = 85, + RICON_BOX_BOTTOM_LEFT = 86, + RICON_BOX_LEFT = 87, + RICON_BOX_TOP_LEFT = 88, + RICON_BOX_CENTER = 89, + RICON_BOX_CIRCLE_MASK = 90, + RICON_POT = 91, + RICON_ALPHA_MULTIPLY = 92, + RICON_ALPHA_CLEAR = 93, + RICON_DITHERING = 94, + RICON_MIPMAPS = 95, + RICON_BOX_GRID = 96, + RICON_GRID = 97, + RICON_BOX_CORNERS_SMALL = 98, + RICON_BOX_CORNERS_BIG = 99, + RICON_FOUR_BOXES = 100, + RICON_GRID_FILL = 101, + RICON_BOX_MULTISIZE = 102, + RICON_ZOOM_SMALL = 103, + RICON_ZOOM_MEDIUM = 104, + RICON_ZOOM_BIG = 105, + RICON_ZOOM_ALL = 106, + RICON_ZOOM_CENTER = 107, + RICON_BOX_DOTS_SMALL = 108, + RICON_BOX_DOTS_BIG = 109, + RICON_BOX_CONCENTRIC = 110, + RICON_BOX_GRID_BIG = 111, + RICON_OK_TICK = 112, + RICON_CROSS = 113, + RICON_ARROW_LEFT = 114, + RICON_ARROW_RIGHT = 115, + RICON_ARROW_BOTTOM = 116, + RICON_ARROW_TOP = 117, + RICON_ARROW_LEFT_FILL = 118, + RICON_ARROW_RIGHT_FILL = 119, + RICON_ARROW_BOTTOM_FILL = 120, + RICON_ARROW_TOP_FILL = 121, + RICON_AUDIO = 122, + RICON_FX = 123, + RICON_WAVE = 124, + RICON_WAVE_SINUS = 125, + RICON_WAVE_SQUARE = 126, + RICON_WAVE_TRIANGULAR = 127, + RICON_CROSS_SMALL = 128, + RICON_PLAYER_PREVIOUS = 129, + RICON_PLAYER_PLAY_BACK = 130, + RICON_PLAYER_PLAY = 131, + RICON_PLAYER_PAUSE = 132, + RICON_PLAYER_STOP = 133, + RICON_PLAYER_NEXT = 134, + RICON_PLAYER_RECORD = 135, + RICON_MAGNET = 136, + RICON_LOCK_CLOSE = 137, + RICON_LOCK_OPEN = 138, + RICON_CLOCK = 139, + RICON_TOOLS = 140, + RICON_GEAR = 141, + RICON_GEAR_BIG = 142, + RICON_BIN = 143, + RICON_HAND_POINTER = 144, + RICON_LASER = 145, + RICON_COIN = 146, + RICON_EXPLOSION = 147, + RICON_1UP = 148, + RICON_PLAYER = 149, + RICON_PLAYER_JUMP = 150, + RICON_KEY = 151, + RICON_DEMON = 152, + RICON_TEXT_POPUP = 153, + RICON_GEAR_EX = 154, + RICON_CRACK = 155, + RICON_CRACK_POINTS = 156, + RICON_STAR = 157, + RICON_DOOR = 158, + RICON_EXIT = 159, + RICON_MODE_2D = 160, + RICON_MODE_3D = 161, + RICON_CUBE = 162, + RICON_CUBE_FACE_TOP = 163, + RICON_CUBE_FACE_LEFT = 164, + RICON_CUBE_FACE_FRONT = 165, + RICON_CUBE_FACE_BOTTOM = 166, + RICON_CUBE_FACE_RIGHT = 167, + RICON_CUBE_FACE_BACK = 168, + RICON_CAMERA = 169, + RICON_SPECIAL = 170, + RICON_LINK_NET = 171, + RICON_LINK_BOXES = 172, + RICON_LINK_MULTI = 173, + RICON_LINK = 174, + RICON_LINK_BROKE = 175, + RICON_TEXT_NOTES = 176, + RICON_NOTEBOOK = 177, + RICON_SUITCASE = 178, + RICON_SUITCASE_ZIP = 179, + RICON_MAILBOX = 180, + RICON_MONITOR = 181, + RICON_PRINTER = 182, + RICON_PHOTO_CAMERA = 183, + RICON_PHOTO_CAMERA_FLASH = 184, + RICON_HOUSE = 185, + RICON_HEART = 186, + RICON_CORNER = 187, + RICON_VERTICAL_BARS = 188, + RICON_VERTICAL_BARS_FILL = 189, + RICON_LIFE_BARS = 190, + RICON_INFO = 191, + RICON_CROSSLINE = 192, + RICON_HELP = 193, + RICON_FILETYPE_ALPHA = 194, + RICON_FILETYPE_HOME = 195, + RICON_LAYERS_VISIBLE = 196, + RICON_LAYERS = 197, + RICON_WINDOW = 198, + RICON_HIDPI = 199, + RICON_200 = 200, + RICON_201 = 201, + RICON_202 = 202, + RICON_203 = 203, + RICON_204 = 204, + RICON_205 = 205, + RICON_206 = 206, + RICON_207 = 207, + RICON_208 = 208, + RICON_209 = 209, + RICON_210 = 210, + RICON_211 = 211, + RICON_212 = 212, + RICON_213 = 213, + RICON_214 = 214, + RICON_215 = 215, + RICON_216 = 216, + RICON_217 = 217, + RICON_218 = 218, + RICON_219 = 219, + RICON_220 = 220, + RICON_221 = 221, + RICON_222 = 222, + RICON_223 = 223, + RICON_224 = 224, + RICON_225 = 225, + RICON_226 = 226, + RICON_227 = 227, + RICON_228 = 228, + RICON_229 = 229, + RICON_230 = 230, + RICON_231 = 231, + RICON_232 = 232, + RICON_233 = 233, + RICON_234 = 234, + RICON_235 = 235, + RICON_236 = 236, + RICON_237 = 237, + RICON_238 = 238, + RICON_239 = 239, + RICON_240 = 240, + RICON_241 = 241, + RICON_242 = 242, + RICON_243 = 243, + RICON_244 = 244, + RICON_245 = 245, + RICON_246 = 246, + RICON_247 = 247, + RICON_248 = 248, + RICON_249 = 249, + RICON_250 = 250, + RICON_251 = 251, + RICON_252 = 252, + RICON_253 = 253, + RICON_254 = 254, + RICON_255 = 255, +} guiIconName; + +#endif // RICONS_H + +#if defined(RICONS_IMPLEMENTATION) +//---------------------------------------------------------------------------------- +// Icons data (allocated on memory data section by default) +// NOTE: A new icon set could be loaded over this array using GuiLoadIcons(), +// just note that loaded icons set must be same RICON_SIZE +//---------------------------------------------------------------------------------- +static unsigned int guiIcons[RICON_MAX_ICONS * RICON_DATA_ELEMENTS] = { + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_NONE + 0x3ff80000, 0x2f082008, 0x2042207e, 0x40027fc2, + 0x40024002, 0x40024002, 0x40024002, 0x00007ffe, // RICON_FOLDER_FILE_OPEN + 0x3ffe0000, 0x44226422, 0x400247e2, 0x5ffa4002, + 0x57ea500a, 0x500a500a, 0x40025ffa, 0x00007ffe, // RICON_FILE_SAVE_CLASSIC + 0x00000000, 0x0042007e, 0x40027fc2, 0x40024002, + 0x41024002, 0x44424282, 0x793e4102, 0x00000100, // RICON_FOLDER_OPEN + 0x00000000, 0x0042007e, 0x40027fc2, 0x40024002, + 0x41024102, 0x44424102, 0x793e4282, 0x00000000, // RICON_FOLDER_SAVE + 0x3ff00000, 0x201c2010, 0x20042004, 0x21042004, + 0x24442284, 0x21042104, 0x20042104, 0x00003ffc, // RICON_FILE_OPEN + 0x3ff00000, 0x201c2010, 0x20042004, 0x21042004, + 0x21042104, 0x22842444, 0x20042104, 0x00003ffc, // RICON_FILE_SAVE + 0x3ff00000, 0x201c2010, 0x00042004, 0x20041004, + 0x20844784, 0x00841384, 0x20042784, 0x00003ffc, // RICON_FILE_EXPORT + 0x3ff00000, 0x201c2010, 0x20042004, 0x20042004, + 0x22042204, 0x22042f84, 0x20042204, 0x00003ffc, // RICON_FILE_NEW + 0x3ff00000, 0x201c2010, 0x20042004, 0x20042004, + 0x25042884, 0x25042204, 0x20042884, 0x00003ffc, // RICON_FILE_DELETE + 0x3ff00000, 0x201c2010, 0x20042004, 0x20042ff4, + 0x20042ff4, 0x20042ff4, 0x20042004, 0x00003ffc, // RICON_FILETYPE_TEXT + 0x3ff00000, 0x201c2010, 0x27042004, 0x244424c4, + 0x26442444, 0x20642664, 0x20042004, 0x00003ffc, // RICON_FILETYPE_AUDIO + 0x3ff00000, 0x201c2010, 0x26042604, 0x20042004, + 0x35442884, 0x2414222c, 0x20042004, 0x00003ffc, // RICON_FILETYPE_IMAGE + 0x3ff00000, 0x201c2010, 0x20c42004, 0x22442144, + 0x22442444, 0x20c42144, 0x20042004, 0x00003ffc, // RICON_FILETYPE_PLAY + 0x3ff00000, 0x3ffc2ff0, 0x3f3c2ff4, 0x3dbc2eb4, + 0x3dbc2bb4, 0x3f3c2eb4, 0x3ffc2ff4, 0x00002ff4, // RICON_FILETYPE_VIDEO + 0x3ff00000, 0x201c2010, 0x21842184, 0x21842004, + 0x21842184, 0x21842184, 0x20042184, 0x00003ffc, // RICON_FILETYPE_INFO + 0x0ff00000, 0x381c0810, 0x28042804, 0x28042804, + 0x28042804, 0x28042804, 0x20102ffc, 0x00003ff0, // RICON_FILE_COPY + 0x00000000, 0x701c0000, 0x079c1e14, 0x55a000f0, + 0x079c00f0, 0x701c1e14, 0x00000000, 0x00000000, // RICON_FILE_CUT + 0x01c00000, 0x13e41bec, 0x3f841004, 0x204420c4, + 0x20442044, 0x20442044, 0x207c2044, 0x00003fc0, // RICON_FILE_PASTE + 0x00000000, 0x3aa00fe0, 0x2abc2aa0, 0x2aa42aa4, + 0x20042aa4, 0x20042004, 0x3ffc2004, 0x00000000, // RICON_CURSOR_HAND + 0x00000000, 0x003c000c, 0x030800c8, 0x30100c10, + 0x10202020, 0x04400840, 0x01800280, 0x00000000, // RICON_CURSOR_POINTER + 0x00000000, 0x00180000, 0x01f00078, 0x03e007f0, + 0x07c003e0, 0x04000e40, 0x00000000, 0x00000000, // RICON_CURSOR_CLASSIC + 0x00000000, 0x04000000, 0x11000a00, 0x04400a80, + 0x01100220, 0x00580088, 0x00000038, 0x00000000, // RICON_PENCIL + 0x04000000, 0x15000a00, 0x50402880, 0x14102820, + 0x05040a08, 0x015c028c, 0x007c00bc, 0x00000000, // RICON_PENCIL_BIG + 0x01c00000, 0x01400140, 0x01400140, 0x0ff80140, + 0x0ff80808, 0x0aa80808, 0x0aa80aa8, 0x00000ff8, // RICON_BRUSH_CLASSIC + 0x1ffc0000, 0x5ffc7ffe, 0x40004000, 0x00807f80, + 0x01c001c0, 0x01c001c0, 0x01c001c0, 0x00000080, // RICON_BRUSH_PAINTER + 0x00000000, 0x00800000, 0x01c00080, 0x03e001c0, + 0x07f003e0, 0x036006f0, 0x000001c0, 0x00000000, // RICON_WATER_DROP + 0x00000000, 0x3e003800, 0x1f803f80, 0x0c201e40, + 0x02080c10, 0x00840104, 0x00380044, 0x00000000, // RICON_COLOR_PICKER + 0x00000000, 0x07800300, 0x1fe00fc0, 0x3f883fd0, + 0x0e021f04, 0x02040402, 0x00f00108, 0x00000000, // RICON_RUBBER + 0x00c00000, 0x02800140, 0x08200440, 0x20081010, + 0x2ffe3004, 0x03f807fc, 0x00e001f0, 0x00000040, // RICON_COLOR_BUCKET + 0x00000000, 0x21843ffc, 0x01800180, 0x01800180, + 0x01800180, 0x01800180, 0x03c00180, 0x00000000, // RICON_TEXT_T + 0x00800000, 0x01400180, 0x06200340, 0x0c100620, + 0x1ff80c10, 0x380c1808, 0x70067004, 0x0000f80f, // RICON_TEXT_A + 0x78000000, 0x50004000, 0x00004800, 0x03c003c0, + 0x03c003c0, 0x00100000, 0x0002000a, 0x0000000e, // RICON_SCALE + 0x75560000, 0x5e004002, 0x54001002, 0x41001202, + 0x408200fe, 0x40820082, 0x40820082, 0x00006afe, // RICON_RESIZE + 0x00000000, 0x3f003f00, 0x3f003f00, 0x3f003f00, + 0x00400080, 0x001c0020, 0x001c001c, 0x00000000, // RICON_FILTER_POINT + 0x6d800000, 0x00004080, 0x40804080, 0x40800000, + 0x00406d80, 0x001c0020, 0x001c001c, 0x00000000, // RICON_FILTER_BILINEAR + 0x40080000, 0x1ffe2008, 0x14081008, 0x11081208, + 0x10481088, 0x10081028, 0x10047ff8, 0x00001002, // RICON_CROP + 0x00100000, 0x3ffc0010, 0x2ab03550, 0x22b02550, + 0x20b02150, 0x20302050, 0x2000fff0, 0x00002000, // RICON_CROP_ALPHA + 0x40000000, 0x1ff82000, 0x04082808, 0x01082208, + 0x00482088, 0x00182028, 0x35542008, 0x00000002, // RICON_SQUARE_TOGGLE + 0x00000000, 0x02800280, 0x06c006c0, 0x0ea00ee0, + 0x1e901eb0, 0x3e883e98, 0x7efc7e8c, 0x00000000, // RICON_SIMMETRY + 0x01000000, 0x05600100, 0x1d480d50, 0x7d423d44, + 0x3d447d42, 0x0d501d48, 0x01000560, 0x00000100, // RICON_SIMMETRY_HORIZONTAL + 0x01800000, 0x04200240, 0x10080810, 0x00001ff8, + 0x00007ffe, 0x0ff01ff8, 0x03c007e0, 0x00000180, // RICON_SIMMETRY_VERTICAL + 0x00000000, 0x010800f0, 0x02040204, 0x02040204, + 0x07f00308, 0x1c000e00, 0x30003800, 0x00000000, // RICON_LENS + 0x00000000, 0x061803f0, 0x08240c0c, 0x08040814, + 0x0c0c0804, 0x23f01618, 0x18002400, 0x00000000, // RICON_LENS_BIG + 0x00000000, 0x00000000, 0x1c7007c0, 0x638e3398, + 0x1c703398, 0x000007c0, 0x00000000, 0x00000000, // RICON_EYE_ON + 0x00000000, 0x10002000, 0x04700fc0, 0x610e3218, + 0x1c703098, 0x001007a0, 0x00000008, 0x00000000, // RICON_EYE_OFF + 0x00000000, 0x00007ffc, 0x40047ffc, 0x10102008, + 0x04400820, 0x02800280, 0x02800280, 0x00000100, // RICON_FILTER_TOP + 0x00000000, 0x40027ffe, 0x10082004, 0x04200810, + 0x02400240, 0x02400240, 0x01400240, 0x000000c0, // RICON_FILTER + 0x00800000, 0x00800080, 0x00000080, 0x3c9e0000, + 0x00000000, 0x00800080, 0x00800080, 0x00000000, // RICON_TARGET_POINT + 0x00800000, 0x00800080, 0x00800080, 0x3f7e01c0, + 0x008001c0, 0x00800080, 0x00800080, 0x00000000, // RICON_TARGET_SMALL + 0x00800000, 0x00800080, 0x03e00080, 0x3e3e0220, + 0x03e00220, 0x00800080, 0x00800080, 0x00000000, // RICON_TARGET_BIG + 0x01000000, 0x04400280, 0x01000100, 0x43842008, + 0x43849ab2, 0x01002008, 0x04400100, 0x01000280, // RICON_TARGET_MOVE + 0x01000000, 0x04400280, 0x01000100, 0x41042108, + 0x41049ff2, 0x01002108, 0x04400100, 0x01000280, // RICON_CURSOR_MOVE + 0x781e0000, 0x500a4002, 0x04204812, 0x00000240, + 0x02400000, 0x48120420, 0x4002500a, 0x0000781e, // RICON_CURSOR_SCALE + 0x00000000, 0x20003c00, 0x24002800, 0x01000200, + 0x00400080, 0x00140024, 0x003c0004, 0x00000000, // RICON_CURSOR_SCALE_RIGHT + 0x00000000, 0x0004003c, 0x00240014, 0x00800040, + 0x02000100, 0x28002400, 0x3c002000, 0x00000000, // RICON_CURSOR_SCALE_LEFT + 0x00000000, 0x00100020, 0x10101fc8, 0x10001020, + 0x10001000, 0x10001000, 0x00001fc0, 0x00000000, // RICON_UNDO + 0x00000000, 0x08000400, 0x080813f8, 0x00080408, + 0x00080008, 0x00080008, 0x000003f8, 0x00000000, // RICON_REDO + 0x00000000, 0x3ffc0000, 0x20042004, 0x20002000, + 0x20402000, 0x3f902020, 0x00400020, 0x00000000, // RICON_REREDO + 0x00000000, 0x3ffc0000, 0x20042004, 0x27fc2004, + 0x20202000, 0x3fc82010, 0x00200010, 0x00000000, // RICON_MUTATE + 0x00000000, 0x0ff00000, 0x10081818, 0x11801008, + 0x10001180, 0x18101020, 0x00100fc8, 0x00000020, // RICON_ROTATE + 0x00000000, 0x04000200, 0x240429fc, 0x20042204, + 0x20442004, 0x3f942024, 0x00400020, 0x00000000, // RICON_REPEAT + 0x00000000, 0x20001000, 0x22104c0e, 0x00801120, + 0x11200040, 0x4c0e2210, 0x10002000, 0x00000000, // RICON_SHUFFLE + 0x7ffe0000, 0x50024002, 0x44024802, 0x41024202, + 0x40424082, 0x40124022, 0x4002400a, 0x00007ffe, // RICON_EMPTYBOX + 0x00800000, 0x03e00080, 0x08080490, 0x3c9e0808, + 0x08080808, 0x03e00490, 0x00800080, 0x00000000, // RICON_TARGET + 0x00800000, 0x00800080, 0x00800080, 0x3ffe01c0, + 0x008001c0, 0x00800080, 0x00800080, 0x00000000, // RICON_TARGET_SMALL_FILL + 0x00800000, 0x00800080, 0x03e00080, 0x3ffe03e0, + 0x03e003e0, 0x00800080, 0x00800080, 0x00000000, // RICON_TARGET_BIG_FILL + 0x01000000, 0x07c00380, 0x01000100, 0x638c2008, + 0x638cfbbe, 0x01002008, 0x07c00100, 0x01000380, // RICON_TARGET_MOVE_FILL + 0x01000000, 0x07c00380, 0x01000100, 0x610c2108, + 0x610cfffe, 0x01002108, 0x07c00100, 0x01000380, // RICON_CURSOR_MOVE_FILL + 0x781e0000, 0x6006700e, 0x04204812, 0x00000240, + 0x02400000, 0x48120420, 0x700e6006, 0x0000781e, // RICON_CURSOR_SCALE_FILL + 0x00000000, 0x38003c00, 0x24003000, 0x01000200, + 0x00400080, 0x000c0024, 0x003c001c, 0x00000000, // RICON_CURSOR_SCALE_RIGHT + 0x00000000, 0x001c003c, 0x0024000c, 0x00800040, + 0x02000100, 0x30002400, 0x3c003800, 0x00000000, // RICON_CURSOR_SCALE_LEFT + 0x00000000, 0x00300020, 0x10301ff8, 0x10001020, + 0x10001000, 0x10001000, 0x00001fc0, 0x00000000, // RICON_UNDO_FILL + 0x00000000, 0x0c000400, 0x0c081ff8, 0x00080408, + 0x00080008, 0x00080008, 0x000003f8, 0x00000000, // RICON_REDO_FILL + 0x00000000, 0x3ffc0000, 0x20042004, 0x20002000, + 0x20402000, 0x3ff02060, 0x00400060, 0x00000000, // RICON_REREDO_FILL + 0x00000000, 0x3ffc0000, 0x20042004, 0x27fc2004, + 0x20202000, 0x3ff82030, 0x00200030, 0x00000000, // RICON_MUTATE_FILL + 0x00000000, 0x0ff00000, 0x10081818, 0x11801008, + 0x10001180, 0x18301020, 0x00300ff8, 0x00000020, // RICON_ROTATE_FILL + 0x00000000, 0x06000200, 0x26042ffc, 0x20042204, + 0x20442004, 0x3ff42064, 0x00400060, 0x00000000, // RICON_REPEAT_FILL + 0x00000000, 0x30001000, 0x32107c0e, 0x00801120, + 0x11200040, 0x7c0e3210, 0x10003000, 0x00000000, // RICON_SHUFFLE_FILL + 0x00000000, 0x30043ffc, 0x24042804, 0x21042204, + 0x20442084, 0x20142024, 0x3ffc200c, 0x00000000, // RICON_EMPTYBOX_SMALL + 0x00000000, 0x20043ffc, 0x20042004, 0x20042004, + 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000, // RICON_BOX + 0x00000000, 0x23c43ffc, 0x23c423c4, 0x200423c4, + 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000, // RICON_BOX_TOP + 0x00000000, 0x3e043ffc, 0x3e043e04, 0x20043e04, + 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000, // RICON_BOX_TOP_RIGHT + 0x00000000, 0x20043ffc, 0x20042004, 0x3e043e04, + 0x3e043e04, 0x20042004, 0x3ffc2004, 0x00000000, // RICON_BOX_RIGHT + 0x00000000, 0x20043ffc, 0x20042004, 0x20042004, + 0x3e042004, 0x3e043e04, 0x3ffc3e04, 0x00000000, // RICON_BOX_BOTTOM_RIGHT + 0x00000000, 0x20043ffc, 0x20042004, 0x20042004, + 0x23c42004, 0x23c423c4, 0x3ffc23c4, 0x00000000, // RICON_BOX_BOTTOM + 0x00000000, 0x20043ffc, 0x20042004, 0x20042004, + 0x207c2004, 0x207c207c, 0x3ffc207c, 0x00000000, // RICON_BOX_BOTTOM_LEFT + 0x00000000, 0x20043ffc, 0x20042004, 0x207c207c, + 0x207c207c, 0x20042004, 0x3ffc2004, 0x00000000, // RICON_BOX_LEFT + 0x00000000, 0x207c3ffc, 0x207c207c, 0x2004207c, + 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000, // RICON_BOX_TOP_LEFT + 0x00000000, 0x20043ffc, 0x20042004, 0x23c423c4, + 0x23c423c4, 0x20042004, 0x3ffc2004, 0x00000000, // RICON_BOX_CIRCLE_MASK + 0x7ffe0000, 0x40024002, 0x47e24182, 0x4ff247e2, + 0x47e24ff2, 0x418247e2, 0x40024002, 0x00007ffe, // RICON_BOX_CENTER + 0x7fff0000, 0x40014001, 0x40014001, 0x49555ddd, + 0x4945495d, 0x400149c5, 0x40014001, 0x00007fff, // RICON_POT + 0x7ffe0000, 0x53327332, 0x44ce4cce, 0x41324332, + 0x404e40ce, 0x48125432, 0x4006540e, 0x00007ffe, // RICON_ALPHA_MULTIPLY + 0x7ffe0000, 0x53327332, 0x44ce4cce, 0x41324332, + 0x5c4e40ce, 0x44124432, 0x40065c0e, 0x00007ffe, // RICON_ALPHA_CLEAR + 0x7ffe0000, 0x42fe417e, 0x42fe417e, 0x42fe417e, + 0x42fe417e, 0x42fe417e, 0x42fe417e, 0x00007ffe, // RICON_DITHERING + 0x07fe0000, 0x1ffa0002, 0x7fea000a, 0x402a402a, + 0x5b2a512a, 0x5128552a, 0x40205128, 0x00007fe0, // RICON_MIPMAPS + 0x00000000, 0x1ff80000, 0x12481248, 0x12481ff8, + 0x1ff81248, 0x12481248, 0x00001ff8, 0x00000000, // RICON_BOX_GRID + 0x12480000, 0x7ffe1248, 0x12481248, 0x12487ffe, + 0x7ffe1248, 0x12481248, 0x12487ffe, 0x00001248, // RICON_GRID + 0x00000000, 0x1c380000, 0x1c3817e8, 0x08100810, + 0x08100810, 0x17e81c38, 0x00001c38, 0x00000000, // RICON_BOX_CORNERS_SMALL + 0x700e0000, 0x700e5ffa, 0x20042004, 0x20042004, + 0x20042004, 0x20042004, 0x5ffa700e, 0x0000700e, // RICON_BOX_CORNERS_BIG + 0x3f7e0000, 0x21422142, 0x21422142, 0x00003f7e, + 0x21423f7e, 0x21422142, 0x3f7e2142, 0x00000000, // RICON_FOUR_BOXES + 0x00000000, 0x3bb80000, 0x3bb83bb8, 0x3bb80000, + 0x3bb83bb8, 0x3bb80000, 0x3bb83bb8, 0x00000000, // RICON_GRID_FILL + 0x7ffe0000, 0x7ffe7ffe, 0x77fe7000, 0x77fe77fe, + 0x777e7700, 0x777e777e, 0x777e777e, 0x0000777e, // RICON_BOX_MULTISIZE + 0x781e0000, 0x40024002, 0x00004002, 0x01800000, + 0x00000180, 0x40020000, 0x40024002, 0x0000781e, // RICON_ZOOM_SMALL + 0x781e0000, 0x40024002, 0x00004002, 0x03c003c0, + 0x03c003c0, 0x40020000, 0x40024002, 0x0000781e, // RICON_ZOOM_MEDIUM + 0x781e0000, 0x40024002, 0x07e04002, 0x07e007e0, + 0x07e007e0, 0x400207e0, 0x40024002, 0x0000781e, // RICON_ZOOM_BIG + 0x781e0000, 0x5ffa4002, 0x1ff85ffa, 0x1ff81ff8, + 0x1ff81ff8, 0x5ffa1ff8, 0x40025ffa, 0x0000781e, // RICON_ZOOM_ALL + 0x00000000, 0x2004381c, 0x00002004, 0x00000000, + 0x00000000, 0x20040000, 0x381c2004, 0x00000000, // RICON_ZOOM_CENTER + 0x00000000, 0x1db80000, 0x10081008, 0x10080000, + 0x00001008, 0x10081008, 0x00001db8, 0x00000000, // RICON_BOX_DOTS_SMALL + 0x35560000, 0x00002002, 0x00002002, 0x00002002, + 0x00002002, 0x00002002, 0x35562002, 0x00000000, // RICON_BOX_DOTS_BIG + 0x7ffe0000, 0x40024002, 0x48124ff2, 0x49924812, + 0x48124992, 0x4ff24812, 0x40024002, 0x00007ffe, // RICON_BOX_CONCENTRIC + 0x00000000, 0x10841ffc, 0x10841084, 0x1ffc1084, + 0x10841084, 0x10841084, 0x00001ffc, 0x00000000, // RICON_BOX_GRID_BIG + 0x00000000, 0x00000000, 0x10000000, 0x04000800, + 0x01040200, 0x00500088, 0x00000020, 0x00000000, // RICON_OK_TICK + 0x00000000, 0x10080000, 0x04200810, 0x01800240, + 0x02400180, 0x08100420, 0x00001008, 0x00000000, // RICON_CROSS + 0x00000000, 0x02000000, 0x00800100, 0x00200040, + 0x00200010, 0x00800040, 0x02000100, 0x00000000, // RICON_ARROW_LEFT + 0x00000000, 0x00400000, 0x01000080, 0x04000200, + 0x04000800, 0x01000200, 0x00400080, 0x00000000, // RICON_ARROW_RIGHT + 0x00000000, 0x00000000, 0x00000000, 0x08081004, + 0x02200410, 0x00800140, 0x00000000, 0x00000000, // RICON_ARROW_BOTTOM + 0x00000000, 0x00000000, 0x01400080, 0x04100220, + 0x10040808, 0x00000000, 0x00000000, 0x00000000, // RICON_ARROW_TOP + 0x00000000, 0x02000000, 0x03800300, 0x03e003c0, + 0x03e003f0, 0x038003c0, 0x02000300, 0x00000000, // RICON_ARROW_LEFT_FILL + 0x00000000, 0x00400000, 0x01c000c0, 0x07c003c0, + 0x07c00fc0, 0x01c003c0, 0x004000c0, 0x00000000, // RICON_ARROW_RIGHT_FILL + 0x00000000, 0x00000000, 0x00000000, 0x0ff81ffc, + 0x03e007f0, 0x008001c0, 0x00000000, 0x00000000, // RICON_ARROW_BOTTOM_FILL + 0x00000000, 0x00000000, 0x01c00080, 0x07f003e0, + 0x1ffc0ff8, 0x00000000, 0x00000000, 0x00000000, // RICON_ARROW_TOP_FILL + 0x00000000, 0x18a008c0, 0x32881290, 0x24822686, + 0x26862482, 0x12903288, 0x08c018a0, 0x00000000, // RICON_AUDIO + 0x00000000, 0x04800780, 0x004000c0, 0x662000f0, + 0x08103c30, 0x130a0e18, 0x0000318e, 0x00000000, // RICON_FX + 0x00000000, 0x00800000, 0x08880888, 0x2aaa0a8a, + 0x0a8a2aaa, 0x08880888, 0x00000080, 0x00000000, // RICON_WAVE + 0x00000000, 0x00600000, 0x01080090, 0x02040108, + 0x42044204, 0x24022402, 0x00001800, 0x00000000, // RICON_WAVE_SINUS + 0x00000000, 0x07f80000, 0x04080408, 0x04080408, + 0x04080408, 0x7c0e0408, 0x00000000, 0x00000000, // RICON_WAVE_SQUARE + 0x00000000, 0x00000000, 0x00a00040, 0x22084110, + 0x08021404, 0x00000000, 0x00000000, 0x00000000, // RICON_WAVE_TRIANGULAR + 0x00000000, 0x00000000, 0x04200000, 0x01800240, + 0x02400180, 0x00000420, 0x00000000, 0x00000000, // RICON_CROSS_SMALL + 0x00000000, 0x18380000, 0x12281428, 0x10a81128, + 0x112810a8, 0x14281228, 0x00001838, 0x00000000, // RICON_PLAYER_PREVIOUS + 0x00000000, 0x18000000, 0x11801600, 0x10181060, + 0x10601018, 0x16001180, 0x00001800, 0x00000000, // RICON_PLAYER_PLAY_BACK + 0x00000000, 0x00180000, 0x01880068, 0x18080608, + 0x06081808, 0x00680188, 0x00000018, 0x00000000, // RICON_PLAYER_PLAY + 0x00000000, 0x1e780000, 0x12481248, 0x12481248, + 0x12481248, 0x12481248, 0x00001e78, 0x00000000, // RICON_PLAYER_PAUSE + 0x00000000, 0x1ff80000, 0x10081008, 0x10081008, + 0x10081008, 0x10081008, 0x00001ff8, 0x00000000, // RICON_PLAYER_STOP + 0x00000000, 0x1c180000, 0x14481428, 0x15081488, + 0x14881508, 0x14281448, 0x00001c18, 0x00000000, // RICON_PLAYER_NEXT + 0x00000000, 0x03c00000, 0x08100420, 0x10081008, + 0x10081008, 0x04200810, 0x000003c0, 0x00000000, // RICON_PLAYER_RECORD + 0x00000000, 0x0c3007e0, 0x13c81818, 0x14281668, + 0x14281428, 0x1c381c38, 0x08102244, 0x00000000, // RICON_MAGNET + 0x07c00000, 0x08200820, 0x3ff80820, 0x23882008, + 0x21082388, 0x20082108, 0x1ff02008, 0x00000000, // RICON_LOCK_CLOSE + 0x07c00000, 0x08000800, 0x3ff80800, 0x23882008, + 0x21082388, 0x20082108, 0x1ff02008, 0x00000000, // RICON_LOCK_OPEN + 0x01c00000, 0x0c180770, 0x3086188c, 0x60832082, + 0x60034781, 0x30062002, 0x0c18180c, 0x01c00770, // RICON_CLOCK + 0x0a200000, 0x1b201b20, 0x04200e20, 0x04200420, + 0x04700420, 0x0e700e70, 0x0e700e70, 0x04200e70, // RICON_TOOLS + 0x01800000, 0x3bdc318c, 0x0ff01ff8, 0x7c3e1e78, + 0x1e787c3e, 0x1ff80ff0, 0x318c3bdc, 0x00000180, // RICON_GEAR + 0x01800000, 0x3ffc318c, 0x1c381ff8, 0x781e1818, + 0x1818781e, 0x1ff81c38, 0x318c3ffc, 0x00000180, // RICON_GEAR_BIG + 0x00000000, 0x08080ff8, 0x08081ffc, 0x0aa80aa8, + 0x0aa80aa8, 0x0aa80aa8, 0x08080aa8, 0x00000ff8, // RICON_BIN + 0x00000000, 0x00000000, 0x20043ffc, 0x08043f84, + 0x04040f84, 0x04040784, 0x000007fc, 0x00000000, // RICON_HAND_POINTER + 0x00000000, 0x24400400, 0x00001480, 0x6efe0e00, + 0x00000e00, 0x24401480, 0x00000400, 0x00000000, // RICON_LASER + 0x00000000, 0x03c00000, 0x08300460, 0x11181118, + 0x11181118, 0x04600830, 0x000003c0, 0x00000000, // RICON_COIN + 0x00000000, 0x10880080, 0x06c00810, 0x366c07e0, + 0x07e00240, 0x00001768, 0x04200240, 0x00000000, // RICON_EXPLOSION + 0x00000000, 0x3d280000, 0x2528252c, 0x3d282528, + 0x05280528, 0x05e80528, 0x00000000, 0x00000000, // RICON_1UP + 0x01800000, 0x03c003c0, 0x018003c0, 0x0ff007e0, + 0x0bd00bd0, 0x0a500bd0, 0x02400240, 0x02400240, // RICON_PLAYER + 0x01800000, 0x03c003c0, 0x118013c0, 0x03c81ff8, + 0x07c003c8, 0x04400440, 0x0c080478, 0x00000000, // RICON_PLAYER_JUMP + 0x3ff80000, 0x30183ff8, 0x30183018, 0x3ff83ff8, + 0x03000300, 0x03c003c0, 0x03e00300, 0x000003e0, // RICON_KEY + 0x3ff80000, 0x3ff83ff8, 0x33983ff8, 0x3ff83398, + 0x3ff83ff8, 0x00000540, 0x0fe00aa0, 0x00000fe0, // RICON_DEMON + 0x00000000, 0x0ff00000, 0x20041008, 0x25442004, + 0x10082004, 0x06000bf0, 0x00000300, 0x00000000, // RICON_TEXT_POPUP + 0x00000000, 0x11440000, 0x07f00be8, 0x1c1c0e38, + 0x1c1c0c18, 0x07f00e38, 0x11440be8, 0x00000000, // RICON_GEAR_EX + 0x00000000, 0x20080000, 0x0c601010, 0x07c00fe0, + 0x07c007c0, 0x0c600fe0, 0x20081010, 0x00000000, // RICON_CRACK + 0x00000000, 0x20080000, 0x0c601010, 0x04400fe0, + 0x04405554, 0x0c600fe0, 0x20081010, 0x00000000, // RICON_CRACK_POINTS + 0x00000000, 0x00800080, 0x01c001c0, 0x1ffc3ffe, + 0x03e007f0, 0x07f003e0, 0x0c180770, 0x00000808, // RICON_STAR + 0x0ff00000, 0x08180810, 0x08100818, 0x0a100810, + 0x08180810, 0x08100818, 0x08100810, 0x00001ff8, // RICON_DOOR + 0x0ff00000, 0x08100810, 0x08100810, 0x10100010, + 0x4f902010, 0x10102010, 0x08100010, 0x00000ff0, // RICON_EXIT + 0x00040000, 0x001f000e, 0x0ef40004, 0x12f41284, + 0x0ef41214, 0x10040004, 0x7ffc3004, 0x10003000, // RICON_MODE_2D + 0x78040000, 0x501f600e, 0x0ef44004, 0x12f41284, + 0x0ef41284, 0x10140004, 0x7ffc300c, 0x10003000, // RICON_MODE_3D + 0x7fe00000, 0x50286030, 0x47fe4804, 0x44224402, + 0x44224422, 0x241275e2, 0x0c06140a, 0x000007fe, // RICON_CUBE + 0x7fe00000, 0x5ff87ff0, 0x47fe4ffc, 0x44224402, + 0x44224422, 0x241275e2, 0x0c06140a, 0x000007fe, // RICON_CUBE_FACE_TOP + 0x7fe00000, 0x50386030, 0x47fe483c, 0x443e443e, + 0x443e443e, 0x241e75fe, 0x0c06140e, 0x000007fe, // RICON_CUBE_FACE_LEFT + 0x7fe00000, 0x50286030, 0x47fe4804, 0x47fe47fe, + 0x47fe47fe, 0x27fe77fe, 0x0ffe17fe, 0x000007fe, // RICON_CUBE_FACE_FRONT + 0x7fe00000, 0x50286030, 0x47fe4804, 0x44224402, + 0x44224422, 0x3ff27fe2, 0x0ffe1ffa, 0x000007fe, // RICON_CUBE_FACE_BOTTOM + 0x7fe00000, 0x70286030, 0x7ffe7804, 0x7c227c02, + 0x7c227c22, 0x3c127de2, 0x0c061c0a, 0x000007fe, // RICON_CUBE_FACE_RIGHT + 0x7fe00000, 0x7fe87ff0, 0x7ffe7fe4, 0x7fe27fe2, + 0x7fe27fe2, 0x24127fe2, 0x0c06140a, 0x000007fe, // RICON_CUBE_FACE_BACK + 0x00000000, 0x2a0233fe, 0x22022602, 0x22022202, + 0x2a022602, 0x00a033fe, 0x02080110, 0x00000000, // RICON_CAMERA + 0x00000000, 0x200c3ffc, 0x000c000c, 0x3ffc000c, + 0x30003000, 0x30003000, 0x3ffc3004, 0x00000000, // RICON_SPECIAL + 0x00000000, 0x0022003e, 0x012201e2, 0x0100013e, + 0x01000100, 0x79000100, 0x4f004900, 0x00007800, // RICON_LINK_NET + 0x00000000, 0x44007c00, 0x45004600, 0x00627cbe, + 0x00620022, 0x45007cbe, 0x44004600, 0x00007c00, // RICON_LINK_BOXES + 0x00000000, 0x0044007c, 0x0010007c, 0x3f100010, + 0x3f1021f0, 0x3f100010, 0x3f0021f0, 0x00000000, // RICON_LINK_MULTI + 0x00000000, 0x0044007c, 0x00440044, 0x0010007c, + 0x00100010, 0x44107c10, 0x440047f0, 0x00007c00, // RICON_LINK + 0x00000000, 0x0044007c, 0x00440044, 0x0000007c, + 0x00000010, 0x44007c10, 0x44004550, 0x00007c00, // RICON_LINK_BROKE + 0x02a00000, 0x22a43ffc, 0x20042004, 0x20042ff4, + 0x20042ff4, 0x20042ff4, 0x20042004, 0x00003ffc, // RICON_TEXT_NOTES + 0x3ffc0000, 0x20042004, 0x245e27c4, 0x27c42444, + 0x2004201e, 0x201e2004, 0x20042004, 0x00003ffc, // RICON_NOTEBOOK + 0x00000000, 0x07e00000, 0x04200420, 0x24243ffc, + 0x24242424, 0x24242424, 0x3ffc2424, 0x00000000, // RICON_SUITCASE + 0x00000000, 0x0fe00000, 0x08200820, 0x40047ffc, + 0x7ffc5554, 0x40045554, 0x7ffc4004, 0x00000000, // RICON_SUITCASE_ZIP + 0x00000000, 0x20043ffc, 0x3ffc2004, 0x13c81008, + 0x100813c8, 0x10081008, 0x1ff81008, 0x00000000, // RICON_MAILBOX + 0x00000000, 0x40027ffe, 0x5ffa5ffa, 0x5ffa5ffa, + 0x40025ffa, 0x03c07ffe, 0x1ff81ff8, 0x00000000, // RICON_MONITOR + 0x0ff00000, 0x6bfe7ffe, 0x7ffe7ffe, 0x68167ffe, + 0x08106816, 0x08100810, 0x0ff00810, 0x00000000, // RICON_PRINTER + 0x3ff80000, 0xfffe2008, 0x870a8002, 0x904a888a, + 0x904a904a, 0x870a888a, 0xfffe8002, 0x00000000, // RICON_PHOTO_CAMERA + 0x0fc00000, 0xfcfe0cd8, 0x8002fffe, 0x84428382, + 0x84428442, 0x80028382, 0xfffe8002, 0x00000000, // RICON_PHOTO_CAMERA_FLASH + 0x00000000, 0x02400180, 0x08100420, 0x20041008, + 0x23c42004, 0x22442244, 0x3ffc2244, 0x00000000, // RICON_HOUSE + 0x00000000, 0x1c700000, 0x3ff83ef8, 0x3ff83ff8, + 0x0fe01ff0, 0x038007c0, 0x00000100, 0x00000000, // RICON_HEART + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x80000000, 0xe000c000, // RICON_CORNER + 0x00000000, 0x14001c00, 0x15c01400, 0x15401540, + 0x155c1540, 0x15541554, 0x1ddc1554, 0x00000000, // RICON_VERTICAL_BARS + 0x00000000, 0x03000300, 0x1b001b00, 0x1b601b60, + 0x1b6c1b60, 0x1b6c1b6c, 0x1b6c1b6c, 0x00000000, // RICON_VERTICAL_BARS_FILL + 0x00000000, 0x00000000, 0x403e7ffe, 0x7ffe403e, + 0x7ffe0000, 0x43fe43fe, 0x00007ffe, 0x00000000, // RICON_LIFE_BARS + 0x7ffc0000, 0x43844004, 0x43844284, 0x43844004, + 0x42844284, 0x42844284, 0x40044384, 0x00007ffc, // RICON_INFO + 0x40008000, 0x10002000, 0x04000800, 0x01000200, + 0x00400080, 0x00100020, 0x00040008, 0x00010002, // RICON_CROSSLINE + 0x00000000, 0x1ff01ff0, 0x18301830, 0x1f001830, + 0x03001f00, 0x00000300, 0x03000300, 0x00000000, // RICON_HELP + 0x3ff00000, 0x2abc3550, 0x2aac3554, 0x2aac3554, + 0x2aac3554, 0x2aac3554, 0x2aac3554, 0x00003ffc, // RICON_FILETYPE_ALPHA + 0x3ff00000, 0x201c2010, 0x22442184, 0x28142424, + 0x29942814, 0x2ff42994, 0x20042004, 0x00003ffc, // RICON_FILETYPE_HOME + 0x07fe0000, 0x04020402, 0x7fe20402, 0x44224422, + 0x44224422, 0x402047fe, 0x40204020, 0x00007fe0, // RICON_LAYERS_VISIBLE + 0x07fe0000, 0x04020402, 0x7c020402, 0x44024402, + 0x44024402, 0x402047fe, 0x40204020, 0x00007fe0, // RICON_LAYERS + 0x00000000, 0x40027ffe, 0x7ffe4002, 0x40024002, + 0x40024002, 0x40024002, 0x7ffe4002, 0x00000000, // RICON_WINDOW + 0x09100000, 0x09f00910, 0x09100910, 0x00000910, + 0x24a2779e, 0x27a224a2, 0x709e20a2, 0x00000000, // RICON_HIDPI + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_200 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_201 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_202 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_203 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_204 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_205 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_206 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_207 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_208 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_209 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_210 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_211 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_212 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_213 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_214 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_215 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_216 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_217 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_218 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_219 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_220 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_221 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_222 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_223 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_224 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_225 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_226 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_227 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_228 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_229 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_230 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_231 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_232 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_233 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_234 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_235 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_236 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_237 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_238 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_239 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_240 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_241 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_242 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_243 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_244 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_245 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_246 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_247 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_248 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_249 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_250 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_251 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_252 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_253 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_254 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_255 +}; +#endif // RICONS_IMPLEMENTATION diff --git a/src/sb_instr_tables.h b/src/sb_instr_tables.h new file mode 100644 index 000000000..a3b6b9e7e --- /dev/null +++ b/src/sb_instr_tables.h @@ -0,0 +1,531 @@ + +#ifndef SB_INSTR_TABLES +#define SB_INSTR_TABLES 1 + +#include + +typedef struct { + sb_opcode_impl_t impl; + const char *opcode_name; + uint8_t flag_mask[5]; + uint8_t length; // in bytes + uint8_t mcycles; + uint8_t mcycles_branch_taken; +} sb_instr_t; + +const static sb_instr_t sb_decode_table[] = { + {0, "NOP", "----", 1, 1, 0}, + {0, "LD BC,u16", "----", 3, 3, 0}, + {0, "LD (BC),A", "----", 1, 2, 0}, + {0, "INC BC", "----", 1, 2, 0}, + {0, "INC B", "Z0H-", 1, 1, 0}, + {0, "DEC B", "Z1H-", 1, 1, 0}, + {0, "LD B,u8", "----", 2, 2, 0}, + {0, "RLCA", "000C", 1, 1, 0}, + {0, "LD (u16),SP", "----", 3, 5, 0}, + {0, "ADD HL,BC", "-0HC", 1, 2, 0}, + {0, "LD A,(BC)", "----", 1, 2, 0}, + {0, "DEC BC", "----", 1, 2, 0}, + {0, "INC C", "Z0H-", 1, 1, 0}, + {0, "DEC C", "Z1H-", 1, 1, 0}, + {0, "LD C,u8", "----", 2, 2, 0}, + {0, "RRCA", "000C", 1, 1, 0}, + {0, "STOP", "----", 2, 1, 0}, + {0, "LD DE,u16", "----", 3, 3, 0}, + {0, "LD (DE),A", "----", 1, 2, 0}, + {0, "INC DE", "----", 1, 2, 0}, + {0, "INC D", "Z0H-", 1, 1, 0}, + {0, "DEC D", "Z1H-", 1, 1, 0}, + {0, "LD D,u8", "----", 2, 2, 0}, + {0, "RLA", "000C", 1, 1, 0}, + {0, "JR i8", "----", 2, 3, 0}, + {0, "ADD HL,DE", "-0HC", 1, 2, 0}, + {0, "LD A,(DE)", "----", 1, 2, 0}, + {0, "DEC DE", "----", 1, 2, 0}, + {0, "INC E", "Z0H-", 1, 1, 0}, + {0, "DEC E", "Z1H-", 1, 1, 0}, + {0, "LD E,u8", "----", 2, 2, 0}, + {0, "RRA", "000C", 1, 1, 0}, + {0, "JR NZ,i8", "----", 2, 2, 3}, + {0, "LD HL,u16", "----", 3, 3, 0}, + {0, "LD (HL+),A", "----", 1, 2, 0}, + {0, "INC HL", "----", 1, 2, 0}, + {0, "INC H", "Z0H-", 1, 1, 0}, + {0, "DEC H", "Z1H-", 1, 1, 0}, + {0, "LD H,u8", "----", 2, 2, 0}, + {0, "DAA", "Z-0C", 1, 1, 0}, + {0, "JR Z,i8", "----", 2, 2, 3}, + {0, "ADD HL,HL", "-0HC", 1, 2, 0}, + {0, "LD A,(HL+)", "----", 1, 2, 0}, + {0, "DEC HL", "----", 1, 2, 0}, + {0, "INC L", "Z0H-", 1, 1, 0}, + {0, "DEC L", "Z1H-", 1, 1, 0}, + {0, "LD L,u8", "----", 2, 2, 0}, + {0, "CPL", "-11-", 1, 1, 0}, + {0, "JR NC,i8", "----", 2, 2, 3}, + {0, "LD SP,u16", "----", 3, 3, 0}, + {0, "LD (HL-),A", "----", 1, 2, 0}, + {0, "INC SP", "----", 1, 2, 0}, + {0, "INC (HL)", "Z0H-", 1, 3, 0}, + {0, "DEC (HL)", "Z1H-", 1, 3, 0}, + {0, "LD (HL),u8", "----", 2, 3, 0}, + {0, "SCF", "-001", 1, 1, 0}, + {0, "JR C,i8", "----", 2, 2, 3}, + {0, "ADD HL,SP", "-0HC", 1, 2, 0}, + {0, "LD A,(HL-)", "----", 1, 2, 0}, + {0, "DEC SP", "----", 1, 2, 0}, + {0, "INC A", "Z0H-", 1, 1, 0}, + {0, "DEC A", "Z1H-", 1, 1, 0}, + {0, "LD A,u8", "----", 2, 2, 0}, + {0, "CCF", "-00C", 1, 1, 0}, + {0, "LD B,B", "----", 1, 1, 0}, + {0, "LD B,C", "----", 1, 1, 0}, + {0, "LD B,D", "----", 1, 1, 0}, + {0, "LD B,E", "----", 1, 1, 0}, + {0, "LD B,H", "----", 1, 1, 0}, + {0, "LD B,L", "----", 1, 1, 0}, + {0, "LD B,(HL)", "----", 1, 2, 0}, + {0, "LD B,A", "----", 1, 1, 0}, + {0, "LD C,B", "----", 1, 1, 0}, + {0, "LD C,C", "----", 1, 1, 0}, + {0, "LD C,D", "----", 1, 1, 0}, + {0, "LD C,E", "----", 1, 1, 0}, + {0, "LD C,H", "----", 1, 1, 0}, + {0, "LD C,L", "----", 1, 1, 0}, + {0, "LD C,(HL)", "----", 1, 2, 0}, + {0, "LD C,A", "----", 1, 1, 0}, + {0, "LD D,B", "----", 1, 1, 0}, + {0, "LD D,C", "----", 1, 1, 0}, + {0, "LD D,D", "----", 1, 1, 0}, + {0, "LD D,E", "----", 1, 1, 0}, + {0, "LD D,H", "----", 1, 1, 0}, + {0, "LD D,L", "----", 1, 1, 0}, + {0, "LD D,(HL)", "----", 1, 2, 0}, + {0, "LD D,A", "----", 1, 1, 0}, + {0, "LD E,B", "----", 1, 1, 0}, + {0, "LD E,C", "----", 1, 1, 0}, + {0, "LD E,D", "----", 1, 1, 0}, + {0, "LD E,E", "----", 1, 1, 0}, + {0, "LD E,H", "----", 1, 1, 0}, + {0, "LD E,L", "----", 1, 1, 0}, + {0, "LD E,(HL)", "----", 1, 2, 0}, + {0, "LD E,A", "----", 1, 1, 0}, + {0, "LD H,B", "----", 1, 1, 0}, + {0, "LD H,C", "----", 1, 1, 0}, + {0, "LD H,D", "----", 1, 1, 0}, + {0, "LD H,E", "----", 1, 1, 0}, + {0, "LD H,H", "----", 1, 1, 0}, + {0, "LD H,L", "----", 1, 1, 0}, + {0, "LD H,(HL)", "----", 1, 2, 0}, + {0, "LD H,A", "----", 1, 1, 0}, + {0, "LD L,B", "----", 1, 1, 0}, + {0, "LD L,C", "----", 1, 1, 0}, + {0, "LD L,D", "----", 1, 1, 0}, + {0, "LD L,E", "----", 1, 1, 0}, + {0, "LD L,H", "----", 1, 1, 0}, + {0, "LD L,L", "----", 1, 1, 0}, + {0, "LD L,(HL)", "----", 1, 2, 0}, + {0, "LD L,A", "----", 1, 1, 0}, + {0, "LD (HL),B", "----", 1, 2, 0}, + {0, "LD (HL),C", "----", 1, 2, 0}, + {0, "LD (HL),D", "----", 1, 2, 0}, + {0, "LD (HL),E", "----", 1, 2, 0}, + {0, "LD (HL),H", "----", 1, 2, 0}, + {0, "LD (HL),L", "----", 1, 2, 0}, + {0, "HALT", "----", 1, 1, 0}, + {0, "LD (HL),A", "----", 1, 2, 0}, + {0, "LD A,B", "----", 1, 1, 0}, + {0, "LD A,C", "----", 1, 1, 0}, + {0, "LD A,D", "----", 1, 1, 0}, + {0, "LD A,E", "----", 1, 1, 0}, + {0, "LD A,H", "----", 1, 1, 0}, + {0, "LD A,L", "----", 1, 1, 0}, + {0, "LD A,(HL)", "----", 1, 2, 0}, + {0, "LD A,A", "----", 1, 1, 0}, + {0, "ADD A,B", "Z0HC", 1, 1, 0}, + {0, "ADD A,C", "Z0HC", 1, 1, 0}, + {0, "ADD A,D", "Z0HC", 1, 1, 0}, + {0, "ADD A,E", "Z0HC", 1, 1, 0}, + {0, "ADD A,H", "Z0HC", 1, 1, 0}, + {0, "ADD A,L", "Z0HC", 1, 1, 0}, + {0, "ADD A,(HL)", "Z0HC", 1, 2, 0}, + {0, "ADD A,A", "Z0HC", 1, 1, 0}, + {0, "ADC A,B", "Z0HC", 1, 1, 0}, + {0, "ADC A,C", "Z0HC", 1, 1, 0}, + {0, "ADC A,D", "Z0HC", 1, 1, 0}, + {0, "ADC A,E", "Z0HC", 1, 1, 0}, + {0, "ADC A,H", "Z0HC", 1, 1, 0}, + {0, "ADC A,L", "Z0HC", 1, 1, 0}, + {0, "ADC A,(HL)", "Z0HC", 1, 2, 0}, + {0, "ADC A,A", "Z0HC", 1, 1, 0}, + {0, "SUB A,B", "Z1HC", 1, 1, 0}, + {0, "SUB A,C", "Z1HC", 1, 1, 0}, + {0, "SUB A,D", "Z1HC", 1, 1, 0}, + {0, "SUB A,E", "Z1HC", 1, 1, 0}, + {0, "SUB A,H", "Z1HC", 1, 1, 0}, + {0, "SUB A,L", "Z1HC", 1, 1, 0}, + {0, "SUB A,(HL)", "Z1HC", 1, 2, 0}, + {0, "SUB A,A", "Z1HC", 1, 1, 0}, + {0, "SBC A,B", "Z1HC", 1, 1, 0}, + {0, "SBC A,C", "Z1HC", 1, 1, 0}, + {0, "SBC A,D", "Z1HC", 1, 1, 0}, + {0, "SBC A,E", "Z1HC", 1, 1, 0}, + {0, "SBC A,H", "Z1HC", 1, 1, 0}, + {0, "SBC A,L", "Z1HC", 1, 1, 0}, + {0, "SBC A,(HL)", "Z1HC", 1, 2, 0}, + {0, "SBC A,A", "Z1HC", 1, 1, 0}, + {0, "AND A,B", "Z010", 1, 1, 0}, + {0, "AND A,C", "Z010", 1, 1, 0}, + {0, "AND A,D", "Z010", 1, 1, 0}, + {0, "AND A,E", "Z010", 1, 1, 0}, + {0, "AND A,H", "Z010", 1, 1, 0}, + {0, "AND A,L", "Z010", 1, 1, 0}, + {0, "AND A,(HL)", "Z010", 1, 2, 0}, + {0, "AND A,A", "Z010", 1, 1, 0}, + {0, "XOR A,B", "Z000", 1, 1, 0}, + {0, "XOR A,C", "Z000", 1, 1, 0}, + {0, "XOR A,D", "Z000", 1, 1, 0}, + {0, "XOR A,E", "Z000", 1, 1, 0}, + {0, "XOR A,H", "Z000", 1, 1, 0}, + {0, "XOR A,L", "Z000", 1, 1, 0}, + {0, "XOR A,(HL)", "Z000", 1, 2, 0}, + {0, "XOR A,A", "Z000", 1, 1, 0}, + {0, "OR A,B", "Z000", 1, 1, 0}, + {0, "OR A,C", "Z000", 1, 1, 0}, + {0, "OR A,D", "Z000", 1, 1, 0}, + {0, "OR A,E", "Z000", 1, 1, 0}, + {0, "OR A,H", "Z000", 1, 1, 0}, + {0, "OR A,L", "Z000", 1, 1, 0}, + {0, "OR A,(HL)", "Z000", 1, 2, 0}, + {0, "OR A,A", "Z000", 1, 1, 0}, + {0, "CP A,B", "Z1HC", 1, 1, 0}, + {0, "CP A,C", "Z1HC", 1, 1, 0}, + {0, "CP A,D", "Z1HC", 1, 1, 0}, + {0, "CP A,E", "Z1HC", 1, 1, 0}, + {0, "CP A,H", "Z1HC", 1, 1, 0}, + {0, "CP A,L", "Z1HC", 1, 1, 0}, + {0, "CP A,(HL)", "Z1HC", 1, 2, 0}, + {0, "CP A,A", "Z1HC", 1, 1, 0}, + {0, "RET NZ", "----", 1, 2, 5}, + {0, "POP BC", "----", 1, 3, 0}, + {0, "JP NZ,u16", "----", 3, 3, 4}, + {0, "JP u16", "----", 3, 4, 0}, + {0, "CALL NZ,u16", "----", 3, 3, 6}, + {0, "PUSH BC", "----", 1, 4, 0}, + {0, "ADD A,u8", "Z0HC", 2, 2, 0}, + {0, "RST 00h", "----", 1, 4, 0}, + {0, "RET Z", "----", 1, 2, 5}, + {0, "RET", "----", 1, 4, 0}, + {0, "JP Z,u16", "----", 3, 3, 4}, + {0, "PREFIX CB", "----", 1, 1, 0}, + {0, "CALL Z,u16", "----", 3, 3, 6}, + {0, "CALL u16", "----", 3, 6, 0}, + {0, "ADC A,u8", "Z0HC", 2, 2, 0}, + {0, "RST 08h", "----", 1, 4, 0}, + {0, "RET NC", "----", 1, 2, 5}, + {0, "POP DE", "----", 1, 3, 0}, + {0, "JP NC,u16", "----", 3, 3, 4}, + {0, "NOP_NO_INSTR", "----", 1, 1, 0}, + {0, "CALL NC,u16", "----", 3, 3, 6}, + {0, "PUSH DE", "----", 1, 4, 0}, + {0, "SUB A,u8", "Z1HC", 2, 2, 0}, + {0, "RST 10h", "----", 1, 4, 0}, + {0, "RET C", "----", 1, 2, 5}, + {0, "RETI", "----", 1, 4, 0}, + {0, "JP C,u16", "----", 3, 3, 4}, + {0, "NOP_NO_INSTR", "----", 1, 1, 0}, + {0, "CALL C,u16", "----", 3, 3, 6}, + {0, "NOP_NO_INSTR", "----", 1, 1, 0}, + {0, "SBC A,u8", "Z1HC", 2, 2, 0}, + {0, "RST 18h", "----", 1, 4, 0}, + {0, "LD (FF00+u8),A", "----", 2, 3, 0}, + {0, "POP HL", "----", 1, 3, 0}, + {0, "LD (FF00+C),A", "----", 1, 2, 0}, + {0, "NOP_NO_INSTR", "----", 1, 1, 0}, + {0, "NOP_NO_INSTR", "----", 1, 1, 0}, + {0, "PUSH HL", "----", 1, 4, 0}, + {0, "AND A,u8", "Z010", 2, 2, 0}, + {0, "RST 20h", "----", 1, 4, 0}, + {0, "ADD SP,i8", "00HC", 2, 4, 0}, + {0, "JP HL", "----", 1, 1, 0}, + {0, "LD (u16),A", "----", 3, 4, 0}, + {0, "NOP_NO_INSTR", "----", 1, 1, 0}, + {0, "NOP_NO_INSTR", "----", 1, 1, 0}, + {0, "NOP_NO_INSTR", "----", 1, 1, 0}, + {0, "XOR A,u8", "Z000", 2, 2, 0}, + {0, "RST 28h", "----", 1, 4, 0}, + {0, "LD A,(FF00+u8)", "----", 2, 3, 0}, + {0, "POP AF", "ZNHC", 1, 3, 0}, + {0, "LD A,(FF00+C)", "----", 1, 2, 0}, + {0, "DI", "----", 1, 1, 0}, + {0, "NOP_NO_INSTR", "----", 1, 1, 0}, + {0, "PUSH AF", "----", 1, 4, 0}, + {0, "OR A,u8", "Z000", 2, 2, 0}, + {0, "RST 30h", "----", 1, 4, 0}, + {0, "LD HL,SP+i8", "00HC", 2, 3, 0}, + {0, "LD SP,HL", "----", 1, 2, 0}, + {0, "LD A,(u16)", "----", 3, 4, 0}, + {0, "EI", "----", 1, 1, 0}, + {0, "NOP_NO_INSTR", "----", 1, 1, 0}, + {0, "NOP_NO_INSTR", "----", 1, 1, 0}, + {0, "CP A,u8", "Z1HC", 2, 2, 0}, + {0, "RST 38h", "----", 1, 4, 0}, + {0, "RLC B", "Z00C", 2, 2, 0}, + {0, "RLC C", "Z00C", 2, 2, 0}, + {0, "RLC D", "Z00C", 2, 2, 0}, + {0, "RLC E", "Z00C", 2, 2, 0}, + {0, "RLC H", "Z00C", 2, 2, 0}, + {0, "RLC L", "Z00C", 2, 2, 0}, + {0, "RLC (HL)", "Z00C", 2, 4, 0}, + {0, "RLC A", "Z00C", 2, 2, 0}, + {0, "RRC B", "Z00C", 2, 2, 0}, + {0, "RRC C", "Z00C", 2, 2, 0}, + {0, "RRC D", "Z00C", 2, 2, 0}, + {0, "RRC E", "Z00C", 2, 2, 0}, + {0, "RRC H", "Z00C", 2, 2, 0}, + {0, "RRC L", "Z00C", 2, 2, 0}, + {0, "RRC (HL)", "Z00C", 2, 4, 0}, + {0, "RRC A", "Z00C", 2, 2, 0}, + {0, "RL B", "Z00C", 2, 2, 0}, + {0, "RL C", "Z00C", 2, 2, 0}, + {0, "RL D", "Z00C", 2, 2, 0}, + {0, "RL E", "Z00C", 2, 2, 0}, + {0, "RL H", "Z00C", 2, 2, 0}, + {0, "RL L", "Z00C", 2, 2, 0}, + {0, "RL (HL)", "Z00C", 2, 4, 0}, + {0, "RL A", "Z00C", 2, 2, 0}, + {0, "RR B", "Z00C", 2, 2, 0}, + {0, "RR C", "Z00C", 2, 2, 0}, + {0, "RR D", "Z00C", 2, 2, 0}, + {0, "RR E", "Z00C", 2, 2, 0}, + {0, "RR H", "Z00C", 2, 2, 0}, + {0, "RR L", "Z00C", 2, 2, 0}, + {0, "RR (HL)", "Z00C", 2, 4, 0}, + {0, "RR A", "Z00C", 2, 2, 0}, + {0, "SLA B", "Z00C", 2, 2, 0}, + {0, "SLA C", "Z00C", 2, 2, 0}, + {0, "SLA D", "Z00C", 2, 2, 0}, + {0, "SLA E", "Z00C", 2, 2, 0}, + {0, "SLA H", "Z00C", 2, 2, 0}, + {0, "SLA L", "Z00C", 2, 2, 0}, + {0, "SLA (HL)", "Z00C", 2, 4, 0}, + {0, "SLA A", "Z00C", 2, 2, 0}, + {0, "SRA B", "Z00C", 2, 2, 0}, + {0, "SRA C", "Z00C", 2, 2, 0}, + {0, "SRA D", "Z00C", 2, 2, 0}, + {0, "SRA E", "Z00C", 2, 2, 0}, + {0, "SRA H", "Z00C", 2, 2, 0}, + {0, "SRA L", "Z00C", 2, 2, 0}, + {0, "SRA (HL)", "Z00C", 2, 4, 0}, + {0, "SRA A", "Z00C", 2, 2, 0}, + {0, "SWAP B", "Z000", 2, 2, 0}, + {0, "SWAP C", "Z000", 2, 2, 0}, + {0, "SWAP D", "Z000", 2, 2, 0}, + {0, "SWAP E", "Z000", 2, 2, 0}, + {0, "SWAP H", "Z000", 2, 2, 0}, + {0, "SWAP L", "Z000", 2, 2, 0}, + {0, "SWAP (HL)", "Z000", 2, 4, 0}, + {0, "SWAP A", "Z000", 2, 2, 0}, + {0, "SRL B", "Z00C", 2, 2, 0}, + {0, "SRL C", "Z00C", 2, 2, 0}, + {0, "SRL D", "Z00C", 2, 2, 0}, + {0, "SRL E", "Z00C", 2, 2, 0}, + {0, "SRL H", "Z00C", 2, 2, 0}, + {0, "SRL L", "Z00C", 2, 2, 0}, + {0, "SRL (HL)", "Z00C", 2, 4, 0}, + {0, "SRL A", "Z00C", 2, 2, 0}, + {0, "BIT 0,B", "Z01-", 2, 2, 0}, + {0, "BIT 0,C", "Z01-", 2, 2, 0}, + {0, "BIT 0,D", "Z01-", 2, 2, 0}, + {0, "BIT 0,E", "Z01-", 2, 2, 0}, + {0, "BIT 0,H", "Z01-", 2, 2, 0}, + {0, "BIT 0,L", "Z01-", 2, 2, 0}, + {0, "BIT 0,(HL)", "Z01-", 2, 3, 0}, + {0, "BIT 0,A", "Z01-", 2, 2, 0}, + {0, "BIT 1,B", "Z01-", 2, 2, 0}, + {0, "BIT 1,C", "Z01-", 2, 2, 0}, + {0, "BIT 1,D", "Z01-", 2, 2, 0}, + {0, "BIT 1,E", "Z01-", 2, 2, 0}, + {0, "BIT 1,H", "Z01-", 2, 2, 0}, + {0, "BIT 1,L", "Z01-", 2, 2, 0}, + {0, "BIT 1,(HL)", "Z01-", 2, 3, 0}, + {0, "BIT 1,A", "Z01-", 2, 2, 0}, + {0, "BIT 2,B", "Z01-", 2, 2, 0}, + {0, "BIT 2,C", "Z01-", 2, 2, 0}, + {0, "BIT 2,D", "Z01-", 2, 2, 0}, + {0, "BIT 2,E", "Z01-", 2, 2, 0}, + {0, "BIT 2,H", "Z01-", 2, 2, 0}, + {0, "BIT 2,L", "Z01-", 2, 2, 0}, + {0, "BIT 2,(HL)", "Z01-", 2, 3, 0}, + {0, "BIT 2,A", "Z01-", 2, 2, 0}, + {0, "BIT 3,B", "Z01-", 2, 2, 0}, + {0, "BIT 3,C", "Z01-", 2, 2, 0}, + {0, "BIT 3,D", "Z01-", 2, 2, 0}, + {0, "BIT 3,E", "Z01-", 2, 2, 0}, + {0, "BIT 3,H", "Z01-", 2, 2, 0}, + {0, "BIT 3,L", "Z01-", 2, 2, 0}, + {0, "BIT 3,(HL)", "Z01-", 2, 3, 0}, + {0, "BIT 3,A", "Z01-", 2, 2, 0}, + {0, "BIT 4,B", "Z01-", 2, 2, 0}, + {0, "BIT 4,C", "Z01-", 2, 2, 0}, + {0, "BIT 4,D", "Z01-", 2, 2, 0}, + {0, "BIT 4,E", "Z01-", 2, 2, 0}, + {0, "BIT 4,H", "Z01-", 2, 2, 0}, + {0, "BIT 4,L", "Z01-", 2, 2, 0}, + {0, "BIT 4,(HL)", "Z01-", 2, 3, 0}, + {0, "BIT 4,A", "Z01-", 2, 2, 0}, + {0, "BIT 5,B", "Z01-", 2, 2, 0}, + {0, "BIT 5,C", "Z01-", 2, 2, 0}, + {0, "BIT 5,D", "Z01-", 2, 2, 0}, + {0, "BIT 5,E", "Z01-", 2, 2, 0}, + {0, "BIT 5,H", "Z01-", 2, 2, 0}, + {0, "BIT 5,L", "Z01-", 2, 2, 0}, + {0, "BIT 5,(HL)", "Z01-", 2, 3, 0}, + {0, "BIT 5,A", "Z01-", 2, 2, 0}, + {0, "BIT 6,B", "Z01-", 2, 2, 0}, + {0, "BIT 6,C", "Z01-", 2, 2, 0}, + {0, "BIT 6,D", "Z01-", 2, 2, 0}, + {0, "BIT 6,E", "Z01-", 2, 2, 0}, + {0, "BIT 6,H", "Z01-", 2, 2, 0}, + {0, "BIT 6,L", "Z01-", 2, 2, 0}, + {0, "BIT 6,(HL)", "Z01-", 2, 3, 0}, + {0, "BIT 6,A", "Z01-", 2, 2, 0}, + {0, "BIT 7,B", "Z01-", 2, 2, 0}, + {0, "BIT 7,C", "Z01-", 2, 2, 0}, + {0, "BIT 7,D", "Z01-", 2, 2, 0}, + {0, "BIT 7,E", "Z01-", 2, 2, 0}, + {0, "BIT 7,H", "Z01-", 2, 2, 0}, + {0, "BIT 7,L", "Z01-", 2, 2, 0}, + {0, "BIT 7,(HL)", "Z01-", 2, 3, 0}, + {0, "BIT 7,A", "Z01-", 2, 2, 0}, + {0, "RES 0,B", "----", 2, 2, 0}, + {0, "RES 0,C", "----", 2, 2, 0}, + {0, "RES 0,D", "----", 2, 2, 0}, + {0, "RES 0,E", "----", 2, 2, 0}, + {0, "RES 0,H", "----", 2, 2, 0}, + {0, "RES 0,L", "----", 2, 2, 0}, + {0, "RES 0,(HL)", "----", 2, 4, 0}, + {0, "RES 0,A", "----", 2, 2, 0}, + {0, "RES 1,B", "----", 2, 2, 0}, + {0, "RES 1,C", "----", 2, 2, 0}, + {0, "RES 1,D", "----", 2, 2, 0}, + {0, "RES 1,E", "----", 2, 2, 0}, + {0, "RES 1,H", "----", 2, 2, 0}, + {0, "RES 1,L", "----", 2, 2, 0}, + {0, "RES 1,(HL)", "----", 2, 4, 0}, + {0, "RES 1,A", "----", 2, 2, 0}, + {0, "RES 2,B", "----", 2, 2, 0}, + {0, "RES 2,C", "----", 2, 2, 0}, + {0, "RES 2,D", "----", 2, 2, 0}, + {0, "RES 2,E", "----", 2, 2, 0}, + {0, "RES 2,H", "----", 2, 2, 0}, + {0, "RES 2,L", "----", 2, 2, 0}, + {0, "RES 2,(HL)", "----", 2, 4, 0}, + {0, "RES 2,A", "----", 2, 2, 0}, + {0, "RES 3,B", "----", 2, 2, 0}, + {0, "RES 3,C", "----", 2, 2, 0}, + {0, "RES 3,D", "----", 2, 2, 0}, + {0, "RES 3,E", "----", 2, 2, 0}, + {0, "RES 3,H", "----", 2, 2, 0}, + {0, "RES 3,L", "----", 2, 2, 0}, + {0, "RES 3,(HL)", "----", 2, 4, 0}, + {0, "RES 3,A", "----", 2, 2, 0}, + {0, "RES 4,B", "----", 2, 2, 0}, + {0, "RES 4,C", "----", 2, 2, 0}, + {0, "RES 4,D", "----", 2, 2, 0}, + {0, "RES 4,E", "----", 2, 2, 0}, + {0, "RES 4,H", "----", 2, 2, 0}, + {0, "RES 4,L", "----", 2, 2, 0}, + {0, "RES 4,(HL)", "----", 2, 4, 0}, + {0, "RES 4,A", "----", 2, 2, 0}, + {0, "RES 5,B", "----", 2, 2, 0}, + {0, "RES 5,C", "----", 2, 2, 0}, + {0, "RES 5,D", "----", 2, 2, 0}, + {0, "RES 5,E", "----", 2, 2, 0}, + {0, "RES 5,H", "----", 2, 2, 0}, + {0, "RES 5,L", "----", 2, 2, 0}, + {0, "RES 5,(HL)", "----", 2, 4, 0}, + {0, "RES 5,A", "----", 2, 2, 0}, + {0, "RES 6,B", "----", 2, 2, 0}, + {0, "RES 6,C", "----", 2, 2, 0}, + {0, "RES 6,D", "----", 2, 2, 0}, + {0, "RES 6,E", "----", 2, 2, 0}, + {0, "RES 6,H", "----", 2, 2, 0}, + {0, "RES 6,L", "----", 2, 2, 0}, + {0, "RES 6,(HL)", "----", 2, 4, 0}, + {0, "RES 6,A", "----", 2, 2, 0}, + {0, "RES 7,B", "----", 2, 2, 0}, + {0, "RES 7,C", "----", 2, 2, 0}, + {0, "RES 7,D", "----", 2, 2, 0}, + {0, "RES 7,E", "----", 2, 2, 0}, + {0, "RES 7,H", "----", 2, 2, 0}, + {0, "RES 7,L", "----", 2, 2, 0}, + {0, "RES 7,(HL)", "----", 2, 4, 0}, + {0, "RES 7,A", "----", 2, 2, 0}, + {0, "SET 0,B", "----", 2, 2, 0}, + {0, "SET 0,C", "----", 2, 2, 0}, + {0, "SET 0,D", "----", 2, 2, 0}, + {0, "SET 0,E", "----", 2, 2, 0}, + {0, "SET 0,H", "----", 2, 2, 0}, + {0, "SET 0,L", "----", 2, 2, 0}, + {0, "SET 0,(HL)", "----", 2, 4, 0}, + {0, "SET 0,A", "----", 2, 2, 0}, + {0, "SET 1,B", "----", 2, 2, 0}, + {0, "SET 1,C", "----", 2, 2, 0}, + {0, "SET 1,D", "----", 2, 2, 0}, + {0, "SET 1,E", "----", 2, 2, 0}, + {0, "SET 1,H", "----", 2, 2, 0}, + {0, "SET 1,L", "----", 2, 2, 0}, + {0, "SET 1,(HL)", "----", 2, 4, 0}, + {0, "SET 1,A", "----", 2, 2, 0}, + {0, "SET 2,B", "----", 2, 2, 0}, + {0, "SET 2,C", "----", 2, 2, 0}, + {0, "SET 2,D", "----", 2, 2, 0}, + {0, "SET 2,E", "----", 2, 2, 0}, + {0, "SET 2,H", "----", 2, 2, 0}, + {0, "SET 2,L", "----", 2, 2, 0}, + {0, "SET 2,(HL)", "----", 2, 4, 0}, + {0, "SET 2,A", "----", 2, 2, 0}, + {0, "SET 3,B", "----", 2, 2, 0}, + {0, "SET 3,C", "----", 2, 2, 0}, + {0, "SET 3,D", "----", 2, 2, 0}, + {0, "SET 3,E", "----", 2, 2, 0}, + {0, "SET 3,H", "----", 2, 2, 0}, + {0, "SET 3,L", "----", 2, 2, 0}, + {0, "SET 3,(HL)", "----", 2, 4, 0}, + {0, "SET 3,A", "----", 2, 2, 0}, + {0, "SET 4,B", "----", 2, 2, 0}, + {0, "SET 4,C", "----", 2, 2, 0}, + {0, "SET 4,D", "----", 2, 2, 0}, + {0, "SET 4,E", "----", 2, 2, 0}, + {0, "SET 4,H", "----", 2, 2, 0}, + {0, "SET 4,L", "----", 2, 2, 0}, + {0, "SET 4,(HL)", "----", 2, 4, 0}, + {0, "SET 4,A", "----", 2, 2, 0}, + {0, "SET 5,B", "----", 2, 2, 0}, + {0, "SET 5,C", "----", 2, 2, 0}, + {0, "SET 5,D", "----", 2, 2, 0}, + {0, "SET 5,E", "----", 2, 2, 0}, + {0, "SET 5,H", "----", 2, 2, 0}, + {0, "SET 5,L", "----", 2, 2, 0}, + {0, "SET 5,(HL)", "----", 2, 4, 0}, + {0, "SET 5,A", "----", 2, 2, 0}, + {0, "SET 6,B", "----", 2, 2, 0}, + {0, "SET 6,C", "----", 2, 2, 0}, + {0, "SET 6,D", "----", 2, 2, 0}, + {0, "SET 6,E", "----", 2, 2, 0}, + {0, "SET 6,H", "----", 2, 2, 0}, + {0, "SET 6,L", "----", 2, 2, 0}, + {0, "SET 6,(HL)", "----", 2, 4, 0}, + {0, "SET 6,A", "----", 2, 2, 0}, + {0, "SET 7,B", "----", 2, 2, 0}, + {0, "SET 7,C", "----", 2, 2, 0}, + {0, "SET 7,D", "----", 2, 2, 0}, + {0, "SET 7,E", "----", 2, 2, 0}, + {0, "SET 7,H", "----", 2, 2, 0}, + {0, "SET 7,L", "----", 2, 2, 0}, + {0, "SET 7,(HL)", "----", 2, 4, 0}, + {0, "SET 7,A", "----", 2, 2, 0}, + +}; +#endif diff --git a/tools/parse_gbops.py b/tools/parse_gbops.py new file mode 100755 index 000000000..7af0d8f28 --- /dev/null +++ b/tools/parse_gbops.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +import pandas as panda +tables = panda.read_html("../docs/gbops%20-%20The%20Game%20Boy%20opcode%20table.html") +opcode_table = tables[0] #Selecting the first table (for example) +opcode_table2 = tables[1] #Selecting the first table (for example) +opcode_table=opcode_table.drop(axis=1,labels="--"); +opcode_table2=opcode_table2.drop(axis=1,labels="--"); + +print(opcode_table); +print(opcode_table2); +opcode_list=[] + +for r_index, row in opcode_table.iterrows(): #iterate over rows + for c_index, value in row.items(): + if isinstance(value, float): value="NOP_NO_INSTR 1 1m ----"; + opcode_list+=[value.replace("\u200b","")] + +for r_index, row in opcode_table2.iterrows(): #iterate over rows + for c_index, value in row.items(): + if isinstance(value, float): value="NOP_NO_INSTR 1 1m ----"; + opcode_list+=[value.replace("\u200b","")] + +print(opcode_list); + +unique_ops ={} + +with open("../src/sb_instr_tables.h","w") as f: + f.write(""" +#ifndef SB_INSTR_TABLES +#define SB_INSTR_TABLES 1 + +#include + +typedef struct{ + sb_opcode_impl_t impl; + const char* opcode_name; + uint8_t flag_mask[5]; + uint8_t length; // in bytes + uint8_t mcycles; + uint8_t mcycles_branch_taken; +}sb_instr_t; + +const static sb_instr_t sb_decode_table[]={ +""") + for op in opcode_list: + op_name = op.split(' ')[0]; + unique_ops[op_name] = True; + splits = op.rsplit(' ', maxsplit=3); + instr_name ='"'+splits[0]+'"'; + taken_latency = "0"; + split_mcycles = splits[2].replace('m','').split('-'); + if len(split_mcycles)>1: + taken_latency=split_mcycles[1]; + non_taken_latency = split_mcycles[0]; + + impl_name = "sgb_"+op_name.lower()+"_impl"; + impl_name ="0"; + f.write(f' {{ {impl_name:16}, {instr_name:16}, "{splits[3]}", {splits[1]}, {non_taken_latency}, {taken_latency} }},\n') + + f.write(""" +}; +#endif +"""); +for op in opcode_list: + unique_ops[op.split(' ')[0]] = True; + splits = op.rsplit(' ', maxsplit=3); + print(splits) + +for op in unique_ops: + print(op);