@usman404/crowjs 1.0.4 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Core/Component.js +207 -1
- package/Core/GUIEvent/KeyboardEvent.js +2 -1
- package/Core/Root.js +40 -30
- package/Frames/DummyFrame.js +14 -12
- package/Frames/Frame.js +117 -57
- package/Frames/FrameComponent.js +9 -2
- package/Frames/GridFrame.js +131 -27
- package/Frames/ScrollFrame.js +154 -40
- package/README.md +8 -8
- package/UIComponents/Button.js +281 -0
- package/UIComponents/Icon.js +374 -0
- package/UIComponents/Input.js +15 -8
- package/UIComponents/Label.js +102 -158
- package/UIComponents/TextComponent.js +838 -0
- package/UIComponents/TextField.js +170 -39
- package/UIComponents/UIComponent.js +68 -35
- package/index.js +5 -1
- package/package.json +9 -2
- package/problems.txt +1 -0
package/Frames/ScrollFrame.js
CHANGED
|
@@ -14,6 +14,7 @@ export class ScrollFrame extends Frame{
|
|
|
14
14
|
* @param {p5.Color} options.highlightedBorderColor - Highlighted border color
|
|
15
15
|
* @param {number} options.borderWidth - Border width
|
|
16
16
|
* @param {number} options.cornerRadius - Corner radius
|
|
17
|
+
* @param {number} options.pad - Unified padding for both axes
|
|
17
18
|
* @param {number} options.padx - Horizontal padding
|
|
18
19
|
* @param {number} options.pady - Vertical padding
|
|
19
20
|
* @param {boolean} options.alwaysShowBanner - Always show banner
|
|
@@ -21,6 +22,8 @@ export class ScrollFrame extends Frame{
|
|
|
21
22
|
* @param {boolean} options.enableHScroll - Enable horizontal scrolling
|
|
22
23
|
* @param {number} options.scrollSensitivity - Scroll speed
|
|
23
24
|
* @param {number} options.bannerHeight - Banner height
|
|
25
|
+
* @param {p5.Color|string} options.bannerColor - Banner background color
|
|
26
|
+
* @param {p5.Color|string} options.bannerDotColor - Banner dot indicator color
|
|
24
27
|
* @param {string} options.alignment - Layout alignment ("v" or "h")
|
|
25
28
|
* @param {number} options.nearestBorderThreshold - Border detection threshold
|
|
26
29
|
* @param {Component|null} options.parent - Parent component
|
|
@@ -29,25 +32,35 @@ export class ScrollFrame extends Frame{
|
|
|
29
32
|
* @param {boolean} options.enableResizing - Allow resizing
|
|
30
33
|
* @param {boolean} options.enableOptimisedResizing - Optimized resizing
|
|
31
34
|
* @param {boolean} options.enableShadow - Enable shadow
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
* @param {string} options.shadowColor - Shadow color (CSS color string)
|
|
36
|
+
* @param {number} options.shadowBlur - Shadow blur radius
|
|
37
|
+
* @param {number} options.shadowOffsetX - Shadow offset on X axis
|
|
38
|
+
* @param {number} options.shadowOffsetY - Shadow offset on Y axis
|
|
39
|
+
* @param {number} options.margin - General margin for all sides
|
|
40
|
+
* @param {number} options.marginx - Horizontal margin (left and right)
|
|
41
|
+
* @param {number} options.marginy - Vertical margin (top and bottom)
|
|
42
|
+
* @param {number} options.marginl - Left margin
|
|
43
|
+
* @param {number} options.marginr - Right margin
|
|
44
|
+
* @param {number} options.margint - Top margin
|
|
45
|
+
* @param {number} options.marginb - Bottom margin
|
|
36
46
|
*/
|
|
37
47
|
constructor(x, y, width, height, {
|
|
38
48
|
id=null,
|
|
39
|
-
backgroundColor = color(
|
|
40
|
-
borderColor = color(
|
|
41
|
-
highlightedBorderColor = color(
|
|
49
|
+
backgroundColor = color('#1e1e2e'),
|
|
50
|
+
borderColor = color('#3a3a4d'),
|
|
51
|
+
highlightedBorderColor = color('#5a5a7a'),
|
|
42
52
|
borderWidth = 1,
|
|
43
|
-
cornerRadius =
|
|
44
|
-
|
|
45
|
-
|
|
53
|
+
cornerRadius = 8,
|
|
54
|
+
pad=null,
|
|
55
|
+
padx=null,
|
|
56
|
+
pady=null,
|
|
46
57
|
alwaysShowBanner = false,
|
|
47
58
|
enableVScroll=false,
|
|
48
59
|
enableHScroll=false,
|
|
49
60
|
scrollSensitivity=20,
|
|
50
61
|
bannerHeight=35,
|
|
62
|
+
bannerColor='#2a2a3d',
|
|
63
|
+
bannerDotColor='#6a6a8a',
|
|
51
64
|
alignment="v", //v for vertical, h for horizontal
|
|
52
65
|
nearestBorderThreshold=8,
|
|
53
66
|
parent=null,
|
|
@@ -56,15 +69,39 @@ export class ScrollFrame extends Frame{
|
|
|
56
69
|
enableResizing=false,
|
|
57
70
|
enableOptimisedResizing=false,
|
|
58
71
|
enableShadow=false,
|
|
59
|
-
shadowColor= '
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
72
|
+
shadowColor= 'rgba(0,0,0,0.5)',
|
|
73
|
+
shadowBlur= 12,
|
|
74
|
+
shadowOffsetX= 0,
|
|
75
|
+
shadowOffsetY= 4,
|
|
76
|
+
margin = 0,
|
|
77
|
+
marginx = null,
|
|
78
|
+
marginy = null,
|
|
79
|
+
marginl = null,
|
|
80
|
+
marginr = null,
|
|
81
|
+
margint = null,
|
|
82
|
+
marginb = null,
|
|
83
|
+
minWidth = 0,
|
|
84
|
+
minHeight = 0,
|
|
85
|
+
showDebugOverlay = false,
|
|
63
86
|
} = {}) {
|
|
87
|
+
if (pad !== null && pad !== undefined) {
|
|
88
|
+
padx = pad;
|
|
89
|
+
pady = pad;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (padx === null || padx === undefined) {
|
|
93
|
+
padx = 0;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (pady === null || pady === undefined) {
|
|
97
|
+
pady = 0;
|
|
98
|
+
}
|
|
99
|
+
|
|
64
100
|
bannerHeight = bannerHeight%height;
|
|
65
101
|
super(x, y, width, height, id, backgroundColor, borderColor, highlightedBorderColor, borderWidth,
|
|
66
|
-
cornerRadius, padx, pady, alwaysShowBanner, bannerHeight, nearestBorderThreshold, parent, "Frame",
|
|
67
|
-
enableReposition, enableOptimisedReposition, enableResizing, enableOptimisedResizing, enableShadow, shadowColor,
|
|
102
|
+
cornerRadius, padx, pady, alwaysShowBanner, bannerHeight, bannerColor, bannerDotColor, nearestBorderThreshold, parent, "Frame",
|
|
103
|
+
enableReposition, enableOptimisedReposition, enableResizing, enableOptimisedResizing, enableShadow, shadowColor, shadowBlur, shadowOffsetX, shadowOffsetY,
|
|
104
|
+
{margin, marginx, marginy, marginl, marginr, margint, marginb, minWidth, minHeight, showDebugOverlay});
|
|
68
105
|
|
|
69
106
|
this.preferences = [];
|
|
70
107
|
//used for calculating weighted dimensions of child elements
|
|
@@ -155,10 +192,10 @@ export class ScrollFrame extends Frame{
|
|
|
155
192
|
//show the top banner
|
|
156
193
|
if(this.alwaysShowBanner || (this.enableReposition && this.isBannerShown)){
|
|
157
194
|
noStroke();
|
|
158
|
-
fill(
|
|
159
|
-
rect(this.x, this.y, this.width, this.bannerHeight);
|
|
195
|
+
fill(this.bannerColor);
|
|
196
|
+
rect(this.x, this.y, this.width, this.bannerHeight, this.cornerRadius, this.cornerRadius, 0, 0);
|
|
160
197
|
|
|
161
|
-
fill(
|
|
198
|
+
fill(this.bannerDotColor);
|
|
162
199
|
ellipse(this.x+this.width/2, this.y+(this.bannerHeight)/2, (this.bannerHeight)/4, (this.bannerHeight)/4);
|
|
163
200
|
ellipse(this.x+this.width/2 - (this.bannerHeight)/2, this.y+(this.bannerHeight)/2, (this.bannerHeight)/4, (this.bannerHeight)/4);
|
|
164
201
|
ellipse(this.x+this.width/2 + (this.bannerHeight)/2, this.y+(this.bannerHeight)/2, (this.bannerHeight)/4, (this.bannerHeight)/4);
|
|
@@ -669,34 +706,104 @@ export class ScrollFrame extends Frame{
|
|
|
669
706
|
}
|
|
670
707
|
}
|
|
671
708
|
}
|
|
709
|
+
|
|
710
|
+
/**
|
|
711
|
+
* Computes the effective minimum width from children's constraints.
|
|
712
|
+
* @returns {number}
|
|
713
|
+
*/
|
|
714
|
+
getEffectiveMinWidth() {
|
|
715
|
+
let minW = this.minWidth;
|
|
716
|
+
let len = this.children.length;
|
|
717
|
+
if (len === 0) return minW;
|
|
718
|
+
|
|
719
|
+
let maxNeeded = 0;
|
|
720
|
+
let invWeight = this.totalWeight > 0 ? 1 / this.totalWeight : 0;
|
|
721
|
+
for (let i = 0; i < len; i++) {
|
|
722
|
+
let curr = this.children[i];
|
|
723
|
+
let pref = this.preferences[i];
|
|
724
|
+
let childMin = curr.getEffectiveMinWidth();
|
|
725
|
+
if (childMin <= 0) continue;
|
|
726
|
+
let childNeeded = childMin + pref[1] + pref[2] + curr.marginl + curr.marginr;
|
|
727
|
+
if (this.alignment === "v") {
|
|
728
|
+
// All children share full width
|
|
729
|
+
maxNeeded = Math.max(maxNeeded, childNeeded + 2 * this.padx);
|
|
730
|
+
} else {
|
|
731
|
+
// Horizontal: proportional distribution
|
|
732
|
+
let weight = pref[0];
|
|
733
|
+
let needed = childNeeded / (weight * invWeight) + 2 * this.padx;
|
|
734
|
+
maxNeeded = Math.max(maxNeeded, needed);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
return Math.max(minW, maxNeeded);
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
/**
|
|
742
|
+
* Computes the effective minimum height from children's constraints.
|
|
743
|
+
* @returns {number}
|
|
744
|
+
*/
|
|
745
|
+
getEffectiveMinHeight() {
|
|
746
|
+
let minH = this.minHeight;
|
|
747
|
+
let len = this.children.length;
|
|
748
|
+
if (len === 0) return minH;
|
|
749
|
+
|
|
750
|
+
let bannerH = (this.alwaysShowBanner || this.enableReposition) ? (this.bannerHeight || 0) : 0;
|
|
751
|
+
|
|
752
|
+
let maxNeeded = 0;
|
|
753
|
+
let invWeight = this.totalWeight > 0 ? 1 / this.totalWeight : 0;
|
|
754
|
+
for (let i = 0; i < len; i++) {
|
|
755
|
+
let curr = this.children[i];
|
|
756
|
+
let pref = this.preferences[i];
|
|
757
|
+
let childMin = curr.getEffectiveMinHeight();
|
|
758
|
+
if (childMin <= 0) continue;
|
|
759
|
+
let childNeeded = childMin + pref[3] + pref[4] + curr.margint + curr.marginb;
|
|
760
|
+
if (this.alignment === "v") {
|
|
761
|
+
// Vertical: proportional distribution
|
|
762
|
+
let weight = pref[0];
|
|
763
|
+
let needed = childNeeded / (weight * invWeight) + 2 * this.pady + bannerH;
|
|
764
|
+
maxNeeded = Math.max(maxNeeded, needed);
|
|
765
|
+
} else {
|
|
766
|
+
// All children share full height
|
|
767
|
+
maxNeeded = Math.max(maxNeeded, childNeeded + 2 * this.pady + bannerH);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
return Math.max(minH, maxNeeded);
|
|
772
|
+
}
|
|
773
|
+
|
|
672
774
|
/**
|
|
673
775
|
* Adjusts child component heights based on weights and alignment
|
|
674
776
|
* @param {number} y - Starting y position
|
|
675
777
|
* @param {number} h - Available height
|
|
676
778
|
*/
|
|
677
779
|
adjustHeight(y, h){
|
|
678
|
-
|
|
679
|
-
|
|
780
|
+
let len = this.children.length;
|
|
781
|
+
let invWeight = this.totalWeight > 0 ? 1 / this.totalWeight : 0;
|
|
782
|
+
for(let i=0; i<len; i++){
|
|
680
783
|
|
|
681
784
|
let curr = this.children[i];
|
|
785
|
+
let pref = this.preferences[i];
|
|
682
786
|
|
|
683
|
-
if(i
|
|
787
|
+
if(i > 0){
|
|
684
788
|
let prev = this.children[i-1];
|
|
685
789
|
if(this.alignment=="v"){
|
|
686
|
-
curr.y = prev.y + prev.height + this.preferences[i-1][4] +
|
|
790
|
+
curr.y = prev.y + prev.height + prev.marginb + this.preferences[i-1][4] + curr.margint + pref[3];
|
|
687
791
|
} else {
|
|
688
|
-
curr.y = y +
|
|
792
|
+
curr.y = y + curr.margint + pref[3];
|
|
689
793
|
}
|
|
690
794
|
} else {
|
|
691
|
-
curr.y = y +
|
|
795
|
+
curr.y = y + curr.margint + pref[3];
|
|
692
796
|
}
|
|
693
797
|
|
|
694
798
|
if(this.enableVScroll==false){
|
|
695
799
|
if(this.alignment=="v"){
|
|
696
|
-
curr.height = (
|
|
800
|
+
curr.height = (pref[0] * invWeight) * h - pref[3] - pref[4] - curr.margint - curr.marginb;
|
|
697
801
|
} else {
|
|
698
|
-
curr.height = h -
|
|
802
|
+
curr.height = h - pref[3] - pref[4] - curr.margint - curr.marginb;
|
|
699
803
|
}
|
|
804
|
+
|
|
805
|
+
// Clamp to child's effective minimum height
|
|
806
|
+
curr.height = Math.max(curr.height, curr.getEffectiveMinHeight());
|
|
700
807
|
}
|
|
701
808
|
|
|
702
809
|
if(curr.type=="Frame"){
|
|
@@ -715,26 +822,31 @@ export class ScrollFrame extends Frame{
|
|
|
715
822
|
* @param {number} w - Available width
|
|
716
823
|
*/
|
|
717
824
|
adjustWidth(x, w){
|
|
718
|
-
|
|
719
|
-
|
|
825
|
+
let len = this.children.length;
|
|
826
|
+
let invWeight = this.totalWeight > 0 ? 1 / this.totalWeight : 0;
|
|
827
|
+
for(let i=0; i<len; i++){
|
|
720
828
|
let curr = this.children[i];
|
|
721
|
-
|
|
829
|
+
let pref = this.preferences[i];
|
|
830
|
+
if(i > 0){
|
|
722
831
|
let prev = this.children[i-1];
|
|
723
832
|
if(this.alignment!="v"){
|
|
724
|
-
curr.x = prev.x + prev.width + this.preferences[i-1][2] +
|
|
833
|
+
curr.x = prev.x + prev.width + prev.marginr + this.preferences[i-1][2] + curr.marginl + pref[1];
|
|
725
834
|
} else {
|
|
726
|
-
curr.x = x +
|
|
835
|
+
curr.x = x + curr.marginl + pref[1];
|
|
727
836
|
}
|
|
728
837
|
} else {
|
|
729
|
-
curr.x = x +
|
|
838
|
+
curr.x = x + curr.marginl + pref[1];
|
|
730
839
|
}
|
|
731
840
|
|
|
732
841
|
if(this.enableHScroll==false){
|
|
733
842
|
if(this.alignment=="v"){
|
|
734
|
-
curr.width = w -
|
|
843
|
+
curr.width = w - pref[1] - pref[2] - curr.marginl - curr.marginr;
|
|
735
844
|
} else {
|
|
736
|
-
curr.width = (
|
|
845
|
+
curr.width = (pref[0] * invWeight) * w - pref[1] - pref[2] - curr.marginl - curr.marginr;
|
|
737
846
|
}
|
|
847
|
+
|
|
848
|
+
// Clamp to child's effective minimum width
|
|
849
|
+
curr.width = Math.max(curr.width, curr.getEffectiveMinWidth());
|
|
738
850
|
}
|
|
739
851
|
|
|
740
852
|
if(curr.type=="Frame"){
|
|
@@ -753,11 +865,13 @@ export class ScrollFrame extends Frame{
|
|
|
753
865
|
* @param {number} yDiff - Y position difference
|
|
754
866
|
*/
|
|
755
867
|
updatePosUtil(xDiff, yDiff){
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
this.children[i]
|
|
759
|
-
|
|
760
|
-
|
|
868
|
+
let len = this.children.length;
|
|
869
|
+
for(let i=0; i<len; i++){
|
|
870
|
+
let child = this.children[i];
|
|
871
|
+
child.x -= xDiff;
|
|
872
|
+
child.y -= yDiff;
|
|
873
|
+
if(child.type==="Frame"){
|
|
874
|
+
child.updatePosUtil(xDiff, yDiff);
|
|
761
875
|
}
|
|
762
876
|
}
|
|
763
877
|
}
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|

|
|
5
5
|
|
|
6
|
-
##
|
|
6
|
+
## Features
|
|
7
7
|
- Minimal and canvas-native UI for p5.js sketches
|
|
8
8
|
- Frames, nested layouts, scrollable & grid containers
|
|
9
9
|
- Customizable UI components: buttons, sliders, toggles, input fields, etc.
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
- Fully drawn inside p5.js (no HTML overlays or DOM interference)
|
|
13
13
|
- Ideal for creative coding, simulations, visual experiments, and tools
|
|
14
14
|
|
|
15
|
-
##
|
|
15
|
+
## Getting Started
|
|
16
16
|
|
|
17
17
|
### 1. Install
|
|
18
18
|
|
|
@@ -99,31 +99,31 @@ window.keyPressed = function(event){
|
|
|
99
99
|
}
|
|
100
100
|
```
|
|
101
101
|
|
|
102
|
-
##
|
|
102
|
+
## Components Overview
|
|
103
103
|
- `Label(x, y, width, height, text, {options})`
|
|
104
104
|
- `ScrollFrame`, `GridFrame`, `Label`, etc.
|
|
105
105
|
|
|
106
106
|
More components and documentation coming soon.
|
|
107
107
|
|
|
108
|
-
##
|
|
108
|
+
## Documentation & Tutorials
|
|
109
109
|
A full **video series** and documentation site are in the works!
|
|
110
110
|
Stay tuned for:
|
|
111
111
|
- Getting started tutorials
|
|
112
112
|
- Custom component creation
|
|
113
113
|
- Internals of CrowJS for contributors
|
|
114
114
|
|
|
115
|
-
##
|
|
115
|
+
## Why CrowJS?
|
|
116
116
|
While p5.js is amazing for generative art and simulations, there's a noticeable gap when it comes to in-sketch UI. CrowJS fills that gap by giving you a canvas-first GUI framework that blends seamlessly with your visuals.
|
|
117
117
|
|
|
118
|
-
##
|
|
118
|
+
## Contributing
|
|
119
119
|
CrowJS is open-source! Contributions, bug reports, and ideas are welcome.
|
|
120
120
|
See the `CONTRIBUTING.md` file for details.
|
|
121
121
|
|
|
122
|
-
##
|
|
122
|
+
## License
|
|
123
123
|
This project is licensed under the MIT License.
|
|
124
124
|
See the `LICENSE` file for details.
|
|
125
125
|
|
|
126
|
-
##
|
|
126
|
+
## Links
|
|
127
127
|
- My sketches: [https://editor.p5js.org/Usman_Ali/sketches/](p5js.org/Usman_Ali/sketches/)
|
|
128
128
|
- p5.js: [https://p5js.org](https://p5js.org)
|
|
129
129
|
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import { TextComponent } from './TextComponent.js';
|
|
2
|
+
import { Component } from '../Core/Component.js';
|
|
3
|
+
|
|
4
|
+
export class Button extends TextComponent {
|
|
5
|
+
/**
|
|
6
|
+
* Creates a button component with hover/press visuals and click behavior
|
|
7
|
+
* @param {number} x - The x-coordinate
|
|
8
|
+
* @param {number} y - The y-coordinate
|
|
9
|
+
* @param {number} width - The width
|
|
10
|
+
* @param {number} height - The height
|
|
11
|
+
* @param {string} label - The text to display
|
|
12
|
+
* @param {Object} options - Configuration options
|
|
13
|
+
* @param {string|null} options.id - Component ID
|
|
14
|
+
* @param {Component|null} options.parent - Parent component
|
|
15
|
+
* @param {p5.Color} options.backgroundColor - Background color
|
|
16
|
+
* @param {p5.Color} options.textColor - Text color
|
|
17
|
+
* @param {p5.Color} options.hoverBackgroundColor - Background on hover
|
|
18
|
+
* @param {p5.Color} options.hoverTextColor - Text color on hover
|
|
19
|
+
* @param {p5.Color} options.pressedBackgroundColor - Background on press
|
|
20
|
+
* @param {p5.Color} options.pressedTextColor - Text color on press
|
|
21
|
+
* @param {boolean} options.borderFlag - Whether to show border
|
|
22
|
+
* @param {p5.Color} options.borderColor - Border color
|
|
23
|
+
* @param {number} options.borderWidth - Border width
|
|
24
|
+
* @param {number} options.cornerRadius - Corner radius
|
|
25
|
+
* @param {boolean} options.enableShadow - Enable shadow rendering
|
|
26
|
+
* @param {string} options.shadowColor - Shadow color (CSS color string)
|
|
27
|
+
* @param {number} options.shadowBlur - Shadow blur radius
|
|
28
|
+
* @param {number} options.shadowOffsetX - Shadow offset on X axis
|
|
29
|
+
* @param {number} options.shadowOffsetY - Shadow offset on Y axis
|
|
30
|
+
* @param {string} options.HTextAlign - Horizontal text alignment
|
|
31
|
+
* @param {string} options.VTextAlign - Vertical text alignment
|
|
32
|
+
* @param {number} options.pad - General padding
|
|
33
|
+
* @param {number} options.padx - Horizontal padding
|
|
34
|
+
* @param {number} options.pady - Vertical padding
|
|
35
|
+
* @param {number} options.padl - Left padding
|
|
36
|
+
* @param {number} options.padr - Right padding
|
|
37
|
+
* @param {number} options.padt - Top padding
|
|
38
|
+
* @param {number} options.padb - Bottom padding
|
|
39
|
+
* @param {boolean} options.wrap - Whether to wrap text
|
|
40
|
+
* @param {string} options.wrapMode - Wrap mode: "word" or "char"
|
|
41
|
+
* @param {string} options.noWrapMode - No-wrap mode: "ellipsis" or "font-size"
|
|
42
|
+
* @param {string} options.ellipsisMode - Ellipsis mode: "leading", "center", or "trailing"
|
|
43
|
+
* @param {p5.Image|null} options.icon - Icon image (null = text only)
|
|
44
|
+
* @param {number} options.iconSize - Icon display size in pixels
|
|
45
|
+
* @param {string} options.iconPosition - "left", "right", "top", or "bottom"
|
|
46
|
+
* @param {number} options.iconGap - Gap between icon and text
|
|
47
|
+
* @param {p5.Color|null} options.iconTintColor - Optional icon tint
|
|
48
|
+
* @param {number} options.iconOpacity - Icon opacity 0-255
|
|
49
|
+
* @param {number} options.margin - General margin for all sides
|
|
50
|
+
* @param {number} options.marginx - Horizontal margin (left and right)
|
|
51
|
+
* @param {number} options.marginy - Vertical margin (top and bottom)
|
|
52
|
+
* @param {number} options.marginl - Left margin
|
|
53
|
+
* @param {number} options.marginr - Right margin
|
|
54
|
+
* @param {number} options.margint - Top margin
|
|
55
|
+
* @param {number} options.marginb - Bottom margin
|
|
56
|
+
* @param {boolean} options.enabled - Whether the button is enabled
|
|
57
|
+
*/
|
|
58
|
+
constructor(x, y, width, height, label,
|
|
59
|
+
{
|
|
60
|
+
id = null,
|
|
61
|
+
parent = null,
|
|
62
|
+
backgroundColor = color('#2a2a3d'),
|
|
63
|
+
textColor = color('#e0e0e0'),
|
|
64
|
+
hoverBackgroundColor = color('#3a3a5a'),
|
|
65
|
+
hoverTextColor = color('#ffffff'),
|
|
66
|
+
pressedBackgroundColor = color('#4a4a6a'),
|
|
67
|
+
pressedTextColor = color('#ffffff'),
|
|
68
|
+
borderFlag = true,
|
|
69
|
+
borderColor = color('#3a3a4d'),
|
|
70
|
+
borderWidth = 1,
|
|
71
|
+
cornerRadius = 8,
|
|
72
|
+
enableShadow = false,
|
|
73
|
+
shadowColor = 'rgba(0,0,0,0.5)',
|
|
74
|
+
shadowBlur = 12,
|
|
75
|
+
shadowOffsetX = 0,
|
|
76
|
+
shadowOffsetY = 4,
|
|
77
|
+
HTextAlign = 'center',
|
|
78
|
+
VTextAlign = 'center',
|
|
79
|
+
pad = 5,
|
|
80
|
+
padx = null,
|
|
81
|
+
pady = null,
|
|
82
|
+
padl = null,
|
|
83
|
+
padr = null,
|
|
84
|
+
padt = null,
|
|
85
|
+
padb = null,
|
|
86
|
+
wrap = true,
|
|
87
|
+
wrapMode = 'word',
|
|
88
|
+
noWrapMode = 'font-size',
|
|
89
|
+
ellipsisMode = 'trailing',
|
|
90
|
+
icon = null,
|
|
91
|
+
iconSize = 20,
|
|
92
|
+
iconPosition = 'left',
|
|
93
|
+
iconGap = 6,
|
|
94
|
+
iconTintColor = null,
|
|
95
|
+
iconOpacity = 255,
|
|
96
|
+
margin = 0,
|
|
97
|
+
marginx = null,
|
|
98
|
+
marginy = null,
|
|
99
|
+
marginl = null,
|
|
100
|
+
marginr = null,
|
|
101
|
+
margint = null,
|
|
102
|
+
marginb = null,
|
|
103
|
+
enabled = true,
|
|
104
|
+
minWidth = 0,
|
|
105
|
+
minHeight = 0,
|
|
106
|
+
showDebugOverlay = false,
|
|
107
|
+
} = {}) {
|
|
108
|
+
super(x, y, width, height, label, {
|
|
109
|
+
id,
|
|
110
|
+
parent,
|
|
111
|
+
backgroundColor,
|
|
112
|
+
textColor,
|
|
113
|
+
borderFlag,
|
|
114
|
+
borderColor,
|
|
115
|
+
borderWidth,
|
|
116
|
+
cornerRadius,
|
|
117
|
+
enableShadow,
|
|
118
|
+
shadowColor,
|
|
119
|
+
shadowBlur,
|
|
120
|
+
shadowOffsetX,
|
|
121
|
+
shadowOffsetY,
|
|
122
|
+
HTextAlign,
|
|
123
|
+
VTextAlign,
|
|
124
|
+
pad,
|
|
125
|
+
padx,
|
|
126
|
+
pady,
|
|
127
|
+
padl,
|
|
128
|
+
padr,
|
|
129
|
+
padt,
|
|
130
|
+
padb,
|
|
131
|
+
wrap,
|
|
132
|
+
wrapMode,
|
|
133
|
+
noWrapMode,
|
|
134
|
+
ellipsisMode,
|
|
135
|
+
icon,
|
|
136
|
+
iconSize,
|
|
137
|
+
iconPosition,
|
|
138
|
+
iconGap,
|
|
139
|
+
iconTintColor,
|
|
140
|
+
iconOpacity,
|
|
141
|
+
margin,
|
|
142
|
+
marginx,
|
|
143
|
+
marginy,
|
|
144
|
+
marginl,
|
|
145
|
+
marginr,
|
|
146
|
+
margint,
|
|
147
|
+
marginb,
|
|
148
|
+
minWidth,
|
|
149
|
+
minHeight,
|
|
150
|
+
showDebugOverlay,
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
this.hoverBackgroundColor = hoverBackgroundColor;
|
|
154
|
+
this.hoverTextColor = hoverTextColor;
|
|
155
|
+
this.pressedBackgroundColor = pressedBackgroundColor;
|
|
156
|
+
this.pressedTextColor = pressedTextColor;
|
|
157
|
+
|
|
158
|
+
this.enabled = enabled;
|
|
159
|
+
this.isHovered = false;
|
|
160
|
+
this.isPressed = false;
|
|
161
|
+
|
|
162
|
+
this.addEventListener('mouseEnter', () => {
|
|
163
|
+
if (!this.enabled) return;
|
|
164
|
+
this.isHovered = true;
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
this.addEventListener('hover', (event) => {
|
|
168
|
+
if (!this.enabled) return;
|
|
169
|
+
cursor('pointer');
|
|
170
|
+
event.stopPropagation();
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
this.addEventListener('mouseLeave', () => {
|
|
174
|
+
this.isHovered = false;
|
|
175
|
+
this.isPressed = false;
|
|
176
|
+
cursor('');
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
this.addEventListener('press', () => {
|
|
180
|
+
if (!this.enabled) return;
|
|
181
|
+
this.isPressed = true;
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
this.addEventListener('release', () => {
|
|
185
|
+
this.isPressed = false;
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Renders the button with hover/press visuals
|
|
191
|
+
*/
|
|
192
|
+
show() {
|
|
193
|
+
if (this.enableShadow) {
|
|
194
|
+
this.drawShadow();
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
push();
|
|
198
|
+
beginClip();
|
|
199
|
+
rect(this.x, this.y, this.width, this.height, this.cornerRadius);
|
|
200
|
+
endClip();
|
|
201
|
+
translate(this.x, this.y);
|
|
202
|
+
|
|
203
|
+
// Background
|
|
204
|
+
fill(this.getBackgroundColor());
|
|
205
|
+
rect(0, 0, this.width, this.height, this.cornerRadius);
|
|
206
|
+
|
|
207
|
+
// Text
|
|
208
|
+
fill(this.getTextColor());
|
|
209
|
+
this.renderText();
|
|
210
|
+
|
|
211
|
+
// Border
|
|
212
|
+
if (this.borderFlag) {
|
|
213
|
+
noFill();
|
|
214
|
+
stroke(this.getBorderColor());
|
|
215
|
+
strokeWeight(this.borderWidth);
|
|
216
|
+
rect(0, 0, this.width, this.height, this.cornerRadius);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
pop();
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Enables or disables the button
|
|
224
|
+
* @param {boolean} enabled - True to enable, false to disable
|
|
225
|
+
*/
|
|
226
|
+
setEnabled(enabled) {
|
|
227
|
+
this.enabled = enabled;
|
|
228
|
+
if (!enabled) {
|
|
229
|
+
this.isHovered = false;
|
|
230
|
+
this.isPressed = false;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
getBackgroundColor() {
|
|
235
|
+
if (!this.enabled) {
|
|
236
|
+
return this.backgroundColor;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (this.isPressed && this.pressedBackgroundColor !== null) {
|
|
240
|
+
return this.pressedBackgroundColor;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (this.isHovered && this.hoverBackgroundColor !== null) {
|
|
244
|
+
return this.hoverBackgroundColor;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return this.backgroundColor;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
getTextColor() {
|
|
251
|
+
if (!this.enabled) {
|
|
252
|
+
return this.textColor;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (this.isPressed && this.pressedTextColor !== null) {
|
|
256
|
+
return this.pressedTextColor;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (this.isHovered && this.hoverTextColor !== null) {
|
|
260
|
+
return this.hoverTextColor;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return this.textColor;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
getBorderColor() {
|
|
267
|
+
if (!this.enabled) {
|
|
268
|
+
return this.borderColor;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (this.isPressed) {
|
|
272
|
+
return color('#6a6a9a');
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (this.isHovered) {
|
|
276
|
+
return color('#5a5a7a');
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return this.borderColor;
|
|
280
|
+
}
|
|
281
|
+
}
|