@usman404/crowjs 1.0.2

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Usman Ali
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,130 @@
1
+ # CrowJS 🐦‍⬛
2
+ **CrowJS** is a lightweight and extensible GUI library built on top of [p5.js](https://p5js.org/), designed for creative coders and simulation builders who need intuitive, canvas-native UI components like buttons, sliders, input fields, and more — all rendered directly inside the p5.js canvas.
3
+
4
+ ![Logo](/crowjs-01-01.png)
5
+
6
+ ## ✨ Features
7
+ - Minimal and canvas-native UI for p5.js sketches
8
+ - Frames, nested layouts, scrollable & grid containers
9
+ - Customizable UI components: buttons, sliders, toggles, input fields, etc.
10
+ - Event system inspired by the web (click, hover, focus, blur)
11
+ - Modular design with support for custom components
12
+ - Fully drawn inside p5.js (no HTML overlays or DOM interference)
13
+ - Ideal for creative coding, simulations, visual experiments, and tools
14
+
15
+ ## 🚀 Getting Started
16
+
17
+ ### 1. Install
18
+
19
+ To use **CrowJS**, begin by downloading this repository and placing the `CrowJS` folder inside your project's source code.
20
+ Once included, open the `index.html` file in your browser; it already contains the required setup for p5.js and for loading CrowJS modules.
21
+
22
+ All GUI development should be done inside **`sketch.js`**, which serves as the main entry point for both your p5.js sketch and all CrowJS components.
23
+
24
+ ---
25
+
26
+ ### ⚠ Important Notes
27
+ - **Do not rename** the `sketch.js` file. CrowJS depends on this filename for consistent module loading.
28
+ - **Do not alter** the structure or script tags inside `index.html`. Modifying them may prevent CrowJS from running correctly.
29
+ - A **CDN-based installation** method will be added in the future to make setup even easier.
30
+
31
+ ---
32
+
33
+ ### 2. Basic Example
34
+ ```javascript
35
+ import { Root } from "./Core/Root.js";
36
+ import { Label } from "./UIComponents/Label.js";
37
+
38
+
39
+ /** @type {Root} */
40
+ let root;
41
+ let clickTimes=1;
42
+
43
+ window.setup = function(){
44
+ createCanvas(windowWidth, windowHeight);
45
+ root = new Root();
46
+
47
+ let btnWidth = 200;
48
+ let btnHeight = 100;
49
+
50
+ let btn = new Label((windowWidth/2)-(btnWidth/2), (windowHeight/2)-(btnHeight/2), 200, 100, "Hello! 👋",
51
+ {cornerRadius: 20,
52
+ backgroundColor: "rgba(0, 0, 0, 1)",
53
+ textColor: "rgba(255, 255, 255, 1)",
54
+ });
55
+
56
+ btn.addEventListener("click", (event)=>{
57
+ clickTimes+=1;
58
+ event.target.setText(`You clicked ${clickTimes} \ntimes!`);
59
+ });
60
+
61
+ root.add(btn);
62
+ }
63
+
64
+ window.draw = function () {
65
+ background('rgba(255,255,255,255)');
66
+ root.show();
67
+ root.mouseEnterEventListeners(mouseX, mouseY);
68
+ root.hoverEventListeners(mouseX, mouseY);
69
+ root.mouseLeaveEventListeners(mouseX, mouseY);
70
+ root.keyDownEventListeners(mouseX, mouseY);
71
+ }
72
+
73
+ window.mouseDragged = function (){
74
+ root.mouseDraggedEventListeners(mouseX, mouseY);
75
+ }
76
+
77
+ window.mouseClicked = function(){
78
+ root.mouseClickedEventListeners(mouseX, mouseY);
79
+ }
80
+
81
+ window.mousePressed = function(){
82
+ root.mousePressedEventListeners(mouseX, mouseY);
83
+ }
84
+
85
+ window.mouseReleased = function(){
86
+ root.mouseReleasedEventListeners(mouseX, mouseY);
87
+ }
88
+
89
+ window.mouseWheel = function(event){
90
+ root.mouseWheelEventListeners(mouseX, mouseY, event);
91
+ }
92
+
93
+ window.keyPressed = function(event){
94
+ if (keyCode === UP_ARROW || keyCode === DOWN_ARROW || keyCode === LEFT_ARROW || keyCode === RIGHT_ARROW) {
95
+ event.preventDefault();
96
+ }
97
+
98
+ root.keyPressedEventListeners(mouseX, mouseY);
99
+ }
100
+ ```
101
+
102
+ ## 🧰 Components Overview
103
+ - `Label(x, y, width, height, text, {options})`
104
+ - `ScrollFrame`, `GridFrame`, `Label`, etc.
105
+
106
+ More components and documentation coming soon.
107
+
108
+ ## 📚 Documentation & Tutorials
109
+ A full **video series** and documentation site are in the works!
110
+ Stay tuned for:
111
+ - Getting started tutorials
112
+ - Custom component creation
113
+ - Internals of CrowJS for contributors
114
+
115
+ ## 💡 Why CrowJS?
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
+
118
+ ## 🤝 Contributing
119
+ CrowJS is open-source! Contributions, bug reports, and ideas are welcome.
120
+ See the `CONTRIBUTING.md` file for details.
121
+
122
+ ## 📄 License
123
+ This project is licensed under the MIT License.
124
+ See the `LICENSE` file for details.
125
+
126
+ ## 🌐 Links
127
+ - My sketches: [https://editor.p5js.org/Usman_Ali/sketches/](p5js.org/Usman_Ali/sketches/)
128
+ - p5.js: [https://p5js.org](https://p5js.org)
129
+
130
+ Created with ❤️ by **Usman**
@@ -0,0 +1,78 @@
1
+ import { GUIEvent } from "../Core/GUIEvent/GUIEvent.js";
2
+ import { UIComponent } from "./UIComponent.js";
3
+ import { Component } from "../Core/Component.js";
4
+
5
+ export class Input extends UIComponent{
6
+ /**
7
+ * Creates a base input component
8
+ * @param {number} x - The x-coordinate
9
+ * @param {number} y - The y-coordinate
10
+ * @param {number} width - The width
11
+ * @param {number} height - The height
12
+ * @param {p5.Color} backgroundColor - Background color
13
+ * @param {boolean} borderFlag - Whether to show border
14
+ * @param {p5.Color} borderColor - Border color
15
+ * @param {number} borderWidth - Border width
16
+ * @param {number} cornerRadius - Corner radius
17
+ * @param {boolean} enableShadow - Enable shadow
18
+ * @param {string} shadowColor - Shadow color
19
+ * @param {number} shadowIntensity - Shadow opacity
20
+ * @param {number} shadowSpread - Shadow spread
21
+ * @param {number} shadowDetail - Shadow layers
22
+ * @param {Object} options - Additional options
23
+ * @param {Component|null} options.parent - Parent component
24
+ * @param {string} options.type - Component type
25
+ * @param {string|null} options.id - Component ID
26
+ */
27
+ constructor(x, y, width, height, backgroundColor, borderFlag, borderColor, borderWidth,
28
+ cornerRadius, enableShadow, shadowColor, shadowIntensity, shadowSpread, shadowDetail,
29
+ {parent=null, type="", id=null} = {}
30
+ ){
31
+ super(x, y, width, height, backgroundColor, borderFlag, borderColor,
32
+ borderWidth, cornerRadius, enableShadow, shadowColor, shadowIntensity,
33
+ shadowSpread, shadowDetail, {parent: parent, type: type, id: id});
34
+ this.isFocused = false;
35
+ // this.addEventListener("focus", (event)=>this.onFocus());
36
+ // this.addEventListener("blur", (event)=>this.onBlur());
37
+ }
38
+
39
+ /**
40
+ * Handles focus event
41
+ * @param {GUIEvent} event - The focus event
42
+ */
43
+ onFocus(event){
44
+ console.log("focused...");
45
+ }
46
+
47
+ /**
48
+ * Handles blur event
49
+ * @param {GUIEvent} event - The blur event
50
+ */
51
+ onBlur(event){
52
+ console.log("blurred...");
53
+ }
54
+ /**
55
+ * Sets focus on the input field with visual feedback
56
+ */
57
+ focus(){
58
+ // console.log("Focus!");
59
+ if(!this.isFocused){
60
+ this.isFocused = true;
61
+ this.borderWidth += 3;
62
+ }
63
+
64
+ this.dispatchEventOnlyOnSelf(new GUIEvent(0, 0, "focus", this));
65
+ }
66
+ /**
67
+ * Removes focus from the input field
68
+ */
69
+ blur(){
70
+ // console.log("Blur!");
71
+ if(this.isFocused){
72
+ this.isFocused = false;
73
+ this.borderWidth -= 3;
74
+ }
75
+
76
+ this.dispatchEventOnlyOnSelf(new GUIEvent(0, 0, "blur", this));
77
+ }
78
+ }
@@ -0,0 +1,234 @@
1
+ import {UIComponent} from './UIComponent.js';
2
+ import { Component } from '../Core/Component.js';
3
+
4
+ export class Label extends UIComponent{
5
+ /**
6
+ * Creates a text label component
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 {boolean} options.borderFlag - Whether to show border
18
+ * @param {p5.Color} options.borderColor - Border color
19
+ * @param {number} options.borderWidth - Border width
20
+ * @param {number} options.cornerRadius - Corner radius
21
+ * @param {boolean} options.enableShadow - Enable shadow rendering
22
+ * @param {string} options.shadowColor - Shadow color
23
+ * @param {number} options.shadowIntensity - Shadow opacity
24
+ * @param {number} options.shadowSpread - Shadow spread
25
+ * @param {number} options.shadowDetail - Shadow layers
26
+ * @param {string} options.HTextAlign - Horizontal text alignment
27
+ * @param {string} options.VTextAlign - Vertical text alignment
28
+ * @param {number} options.pad - General padding
29
+ * @param {number} options.padx - Horizontal padding
30
+ * @param {number} options.pady - Vertical padding
31
+ * @param {number} options.padl - Left padding
32
+ * @param {number} options.padr - Right padding
33
+ * @param {number} options.padt - Top padding
34
+ * @param {number} options.padb - Bottom padding
35
+ */
36
+ constructor(x, y, width, height, label,
37
+ {id=null,
38
+ parent = null,
39
+ backgroundColor = color(200),
40
+ textColor = color(0),
41
+ borderFlag = true,
42
+ borderColor = color(0),
43
+ borderWidth = 1,
44
+ cornerRadius = 0,
45
+ enableShadow=false,
46
+ shadowColor= 'rgb(0,0,0)',
47
+ shadowIntensity= 0.4,
48
+ shadowSpread= 3,
49
+ shadowDetail=5,
50
+ HTextAlign="center",
51
+ VTextAlign="center",
52
+ pad = 5,
53
+ padx = 0,
54
+ pady = 0,
55
+ padl = 0,
56
+ padr = 0,
57
+ padt = 0,
58
+ padb = 0,
59
+ } = {}) {
60
+ super(x, y, width, height, backgroundColor, borderFlag, borderColor,
61
+ borderWidth, cornerRadius, enableShadow, shadowColor, shadowIntensity,
62
+ shadowSpread, shadowDetail, {parent: parent, type: "UIComponent", id: id});
63
+
64
+ this.text = label;
65
+ this.labelSize = 20;
66
+ this.textColor = textColor;
67
+
68
+ this.HTextAlign = HTextAlign;
69
+ this.VTextAlign = VTextAlign;
70
+
71
+ this.pad = pad;
72
+ this.padx = padx;
73
+ this.pady = pady;
74
+ this.padl = padl;
75
+ this.padr = padr;
76
+ this.padt = padt;
77
+ this.padb = padb;
78
+ }
79
+
80
+ // if(this.enableShadow){
81
+ // this.shadowColor = shadowColor;//rgb value
82
+ // this.shadowIntensity = shadowIntensity;//opacity value between 0 and 1
83
+ // this.shadowSpread = shadowSpread;//stroke width of each of those rectangles
84
+ // this.shadowDetail = shadowDetail;//number of rectangles that will be drawn around the component
85
+ // }
86
+ /**
87
+ * Renders the label with text, background, and border
88
+ */
89
+ show() {
90
+ if(this.enableShadow){
91
+ this.drawShadow();
92
+ }
93
+
94
+ push();
95
+ beginClip();
96
+ rect(this.x, this.y, this.width, this.height, this.cornerRadius);
97
+ endClip();
98
+ translate(this.x, this.y);
99
+ fill(this.backgroundColor);
100
+
101
+ // Background rectangle
102
+ rect(0, 0, this.width, this.height, this.cornerRadius);
103
+
104
+ // Text
105
+ fill(this.textColor);
106
+ // textAlign(CENTER, CENTER);
107
+ textSize(this.labelSize);
108
+
109
+ let x;
110
+ if(this.HTextAlign === "left"){
111
+ textAlign(LEFT, CENTER);
112
+ x = this.pad;
113
+ } else if(this.HTextAlign === "right"){
114
+ textAlign(RIGHT, CENTER);
115
+ x = this.width - this.pad;
116
+ } else{
117
+ //center
118
+ textAlign(CENTER, CENTER);
119
+ x = this.width / 2;
120
+ }
121
+
122
+ let y;
123
+ if(this.VTextAlign === "top"){
124
+ textAlign(this.getHTextAlign(), BOTTOM);
125
+ y = this.labelSize + this.pad;
126
+ } else if(this.VTextAlign === "bottom"){
127
+ textAlign(this.getHTextAlign(), TOP);
128
+ y = this.height - this.labelSize - this.pad;
129
+ } else {
130
+ //center
131
+ textAlign(this.getHTextAlign(), CENTER);
132
+ y = this.height / 2;
133
+ }
134
+
135
+ text(this.text, x, y);
136
+ // rect(x, y, this.labelSize, this.labelSize);
137
+
138
+ // Border
139
+ if (this.borderFlag) {
140
+ noFill(); // Only for the border
141
+ stroke(this.borderColor);
142
+ strokeWeight(this.borderWidth);
143
+ rect(0, 0, this.width, this.height, this.cornerRadius); // Fix here
144
+ }
145
+
146
+ pop();
147
+ }
148
+
149
+ /**
150
+ * Converts horizontal alignment string to P5 constant
151
+ * @returns {number} P5 alignment constant
152
+ */
153
+ getHTextAlign(){
154
+ switch(this.HTextAlign){
155
+ case "left":
156
+ return LEFT;
157
+ case "right":
158
+ return RIGHT;
159
+ default:
160
+ return CENTER;
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Converts vertical alignment string to P5 constant
166
+ * @returns {number} P5 alignment constant
167
+ */
168
+ getVTextAlign(){
169
+ switch(this.VTextAlign){
170
+ case "top":
171
+ return TOP;
172
+ case "bottom":
173
+ return BOTTOM;
174
+ default:
175
+ return CENTER;
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Updates the label text and recalculates text size
181
+ * @param {string} text - The new text to display
182
+ */
183
+ setText(text){
184
+ this.text = text;
185
+ this.updateLabelSize();
186
+ }
187
+
188
+ //needs heavy optimizations
189
+ /**
190
+ * Dynamically calculates the optimal text size to fit the container
191
+ * Uses binary search for efficient size calculation
192
+ */
193
+ updateLabelSize() {
194
+ let maxSize = min(this.width * 0.9, this.height * 0.8);
195
+ let minSize = 1;
196
+ let low = minSize;
197
+ let high = maxSize;
198
+ let bestSize = minSize;
199
+
200
+ const maxLabelWidth = this.width * 0.9;
201
+ const maxLabelHeight = this.height * 0.8;
202
+
203
+ while (low <= high) {
204
+ let mid = Math.floor((low + high) / 2);
205
+ textSize(mid);
206
+ let labelWidth = textWidth(this.text);
207
+ let labelHeight = textAscent() + textDescent();
208
+
209
+ if (labelWidth <= maxLabelWidth && labelHeight <= maxLabelHeight) {
210
+ bestSize = mid;
211
+ low = mid + 1;
212
+ } else {
213
+ high = mid - 1;
214
+ }
215
+ }
216
+
217
+ this.labelSize = bestSize;
218
+ }
219
+
220
+ /**
221
+ * Handles width changes and updates text size accordingly
222
+ */
223
+ updateWidth(){
224
+ this.updateLabelSize();
225
+ }
226
+
227
+ /**
228
+ * Handles height changes and updates text size accordingly
229
+ */
230
+ updateHeight(){
231
+ this.updateLabelSize();
232
+ }
233
+ }
234
+