Author: Daksh Doshi (daksh4469)
sugarlabs/musicblocks
- Project Title: MusicBlocksv4 Menu Framework and Connections with Artboard
- Organization: Sugarlabs
- Mentors : Anindya Kundu
Walter Bender
Devin Ulibarri
Peace Ojemeh
MusicBlocks is going through a complete overhaul, allowing us to work on a new, improved version with the latest tech stacks and enhanced performance. It has been a fantastic experience participating in Google Summer of Code 2021 with Sugar Labs. I am very grateful to Sugar Labs for guiding me throughout the project and giving me the opportunity to work alongside an amazing peer group.
The Toolbar in MusicBlocksv3 needed a refactor in terms of both design and the internal implementation. The functionalities in the toolbar needed to be better organized in a way that would enhance the User Experience and provide the most suitable accessibility to them. The latest version of MusicBlocks would have a more maintainable Menu catering to all the issues in the previous version with a new implementation.
The first step of the project was to explore various architectures that would be the best fit for our tech stack involving React and Typescript. After various discussions with mentors, MVVM(Model-View-ViewModel) Architecture was found to be the perfect fit for our project meeting all our requirements. Thereafter, the project structure was decided to be consisting of various components like Menus, Palettes, and Block Framework with the Monitor connecting all of them.
The first step of this project was to decide upon the necessary functionalities to be kept in the new version based on user experiences. Thus, after discussion with mentors and fellow GSoC students, we decided to get rid of the “Modes” in MusicBlocks which were present in Basic and Advanced forms in version 3. Instead, we decided to go with the approach of “High-Shelf” and “Low-Shelf” organization of the Menu dock. High Shelf category would contain the features which a user does not access generally or more than once usually. An example of this would be the “Language Selection” feature. Low Shelf category would contain the feature that a user needs in hand regularly while building a project in Music Blocks.
Using Figma as a prototyping tool, I designed a prototype framework keeping these requirements in mind. These mockups can be found here. This is a result of numerous discussions and brainstorming with mentors to achieve a clean design with optimal compactness.
The Model Component of the Menu Framework is defined in the Menu.ts
file, the View-Model Component in the Menu.ts
file in the components directory and the View Component is defined in the Menu.tsx
and Checkbox.tsx
files inside the views/menu
directory. The View-Model maintains the state and the required methods according to the Model and serves them to the View Component. In simpler terms, the View-Model components acts as a link between the View and the Model Component. The directory structure regarding the Menu Framework Implementation is as follows:
The complete documentation of the Menu Framework can be found here.
The Menu now auto-hides itself when the mouse cursor is not in its near vicinity. This is implemented using an overlay which tracks the movement of mouse pointer and updates the states autoHide
and autoHideTemp
of the Menu whenever the cursor enters or leaves through the boundary of overlay. This feature is developed to increase the working space of the user substantially. A user can this way, access the Menu Dock only when he feels the need to do so and does not have to always get his workspace blocked by the Menu.
The Global Settings of MusicBlocksv4 are stored in the form an object in the Context API and is implemented using the useContext()
hook of the React Framework. The structure(interface) of this config
object is defined as:
interface IConfig {
theme: 'light' | 'dark';
language: string;
horizontalScroll: boolean;
turtleWrap: boolean;
blockSize: number;
masterVolume: number;
}
The Config object is initialized as a state in the App.tsx
file using the useState()
hook. It is then passed on to the children components of the application using the Provider
React Component that is provided for every defined Context. The attributes of this API are then utilized in the children using the Consumer
React Component. The values of these attributes can be updated through the Settings
and Music Settings
tabs in the Menu Dock. The Config object of the Context API can be utilised in any child component in the following manner:
const { config, setConfig } = useContext(ContextConfig);
While a project is running, the turtle can possibly go out of the window parameters. Hence, a Turtle Wrap
can be enabled to handle this case so that, whenever a turtle goes out of the window parameters, its coordinates get reset to the side opposite to the current side from where it has gone out of bounds. This can enable a user to view the complete artwork of the turtle by limiting its area to the window size. We have maintained a turtleWrap
parameter in the Config object of the Context API that maintains the state of enabling the Turtle Wrap.
The Wrap functionality is implemented in the draw()
method in the ArtboardSketch.tsx
file of the ArtBoard Component, which maintains the Artwork of an individual turtle, and this is done by comparing the boolean config.turtleWrap
at every step of sketching the artwork of every individual turtle.
The Clean functionality has also been implemented so that the artwork of each and every turtle can be cleaned through the Menu Dock. This is done by defining a method in Artboard.tsx
to call the clean()
methods in the ArtboardSketch.tsx
file for every turtle in the Artboard. The artwork of every turtle is cleaned by a utility function of the p5
library which provides us with the option to clean the sketch by calling sketch.clear()
for every artwork on the artboard.
The Monitor.ts
file contains the classes Menu
and Artboard
based off of the interfaces IMenu
and IArtboard
and these classes are intantiated in the default constructor of the Monitor
class wherein, the menu and artboard are present as member variables under the private
access modifier(as _menu
and _artboard
respectively). Hence, the menu and artboard in the entire application can only be accessed by their repsective getter
functions defined in the Monitor
class. Now, various methods to update the Config State of the Context API from the children components are registered in the Monitor from various files. For example: The method to update the state of Turtle Wrap from the Menu dock needs to be registerd in the Menu through the Monitor as follows:
registerUpdateWrap(updateTurtleWrap: (isWrapOn: boolean) => void): void {
this._menu.updateTurtleWrap = updateTurtleWrap;
}
In this way, two methods for the same functionality are registered from two different components which need to be connected. For Example: The cleanArtwork()
method that is called from the Menu View is registered from the Menu Component and the clean()
method is registered from the Artboard component and in thus, these two components are communicate with each other for the Clean functionality by the following:
registerClean(): void {
this._menu.cleanArtwork = this._artboard.clean;
}
Pull Request | Description |
---|---|
#72: Menu Framework |
|
#77: App Configurations in Menu Framework |
|
#82: Music Configurations in Menu Framework |
|
#88: Connect Menu and Artboard framework with Monitor |
|
#93: Menu Documentation |
|
On a final note, I'd like to thank all my mentors: Anindya Kundu, Walter Bender, Devin Ulibarri, and Peace Ojemeh for guiding me through the thought process of building this project and providing their valuable feedback in every stage of the process. I learnt a lot about writing quality and maintainable code through this project. I am also grateful to all my peers: Joykirat Singh, Kumar Saurabh Raj, and Chandan Prakash as this project was not a stand-alone one and needed cooperation and valuable help from everyone. Lastly, I'd like to acknowledge Google for conducting such an amazing program.
Looking forward to contributing more, in any way I can.