Skip to content

Commit

Permalink
Merge pull request #627 from tuanchauict/app-ui-state
Browse files Browse the repository at this point in the history
Improve UI state manager
  • Loading branch information
tuanchauict authored Dec 13, 2024
2 parents 1cd12c8 + 83f04ef commit 59dc48a
Show file tree
Hide file tree
Showing 16 changed files with 194 additions and 133 deletions.
7 changes: 2 additions & 5 deletions monosketch-svelte/src/app/app-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ShapeManager } from "$mono/shape/shape-manager";
import { WorkspaceDao } from "$mono/store-manager/dao/workspace-dao";
import { BrowserManager } from "$mono/window/browser-manager";
import { LifecycleOwner } from 'lib/libs/flow';
import { AppUiStateManager } from '$mono/ui-state-manager';
import { AppUiStateManager } from '$mono/ui-state-manager/app-ui-state-manager';

/**
* Main class of the app to handle all kinds of events, UI, actions, etc.
Expand All @@ -21,10 +21,7 @@ export class AppContext {

this.init();

this.appUiStateManager.observeTheme(() => {
console.log('Theme changed');
// TODO: Update theme in the workspace
});
this.appUiStateManager.observeTheme();
};

private init() {
Expand Down
2 changes: 1 addition & 1 deletion monosketch-svelte/src/lib/mono/shape/shape/line.ts
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ export class Line extends AbstractShape {
return this.edges.some(edge => edge.contains(point));
}

isVertex(point: Point): boolean {
isVertex(_point: Point): boolean {
return false; // TODO: Correct this to any of its joint points
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { DEBUG_MODE } from "$mono/build_environment";
import { StorageDocument, StoreKeys } from "$mono/store-manager";
import type { UiStatePayloadType } from "$mono/ui-state-manager/ui-state-payload";
import { Flow, LifecycleOwner } from 'lib/libs/flow';
import { ScrollMode, type ThemeColor, type ThemeMode } from '$mono/ui-state-manager/states';
import { AppThemeManager } from '$mono/ui-state-manager/theme-manager';
import { ScrollModeManager } from '$mono/ui-state-manager/scroll-mode-manager';
import { PanelVisibilityManager } from '$mono/ui-state-manager/panel-visibility-manager';
import { type KeyCommand, KeyCommandController } from '$mono/keycommand';

/**
* A domain class for managing UI state of the app.
* This does not include the state of the workspace.
*/
export class AppUiStateManager {
private appThemeManager = new AppThemeManager();
private scrollModeManager = new ScrollModeManager();
private panelVisibilityManager = new PanelVisibilityManager();
private keyCommandController = new KeyCommandController(document.body);
private settingDocument: StorageDocument = StorageDocument.get(StoreKeys.SETTINGS);

themeModeFlow: Flow<ThemeMode> = this.appThemeManager.themeModeFlow;
scrollModeFlow: Flow<ScrollMode> = this.scrollModeManager.scrollModeFlow;
shapeFormatPanelVisibilityFlow: Flow<boolean> =
this.panelVisibilityManager.shapeFormatPanelVisibilityFlow;
keyCommandFlow: Flow<KeyCommand> = this.keyCommandController.keyCommandFlow;

private fontSizeMutableFlow = new Flow<number>(parseInt(this.settingDocument.get(StoreKeys.FONT_SIZE, "13")!));
fontSizeFlow: Flow<number> = this.fontSizeMutableFlow.distinctUntilChanged();

/**
* A flow that emits true when the font is ready to use.
*/
private fontReadyMutableFlow = new Flow<boolean>(false);
fontReadyFlow: Flow<boolean> = this.fontReadyMutableFlow.immutable();

constructor(
private appLifecycleOwner: LifecycleOwner,
) {
const workspaceFont = new FontFace(
'JetBrainsMono-Regular',
`url('/src/assets/fonts/JetBrainsMono-Regular.woff2')`,
);
workspaceFont.load().then(() => {
document.fonts.add(workspaceFont);
this.fontReadyMutableFlow.value = true;
if (DEBUG_MODE) {
console.log('Font loaded');
}
});
}

observeTheme = (): void => {
this.appThemeManager.observeTheme(this.appLifecycleOwner);
};

getThemedColorCode = (color: ThemeColor): string => {
return this.appThemeManager.getThemedColorCode(color);
};

updateUiState = (payload: UiStatePayloadType): void => {
switch (payload.type) {
case 'ShapeToolVisibility':
this.panelVisibilityManager.setShapeFormatPanelVisibility(payload.isVisible);
break;
case 'ChangeScrollMode':
this.scrollModeManager.setScrollMode(payload.scrollMode);
break;
case 'ChangeTheme':
this.appThemeManager.setTheme(payload.themeMode);
break;
case 'ChangeFontSize':
this.changeFontSize(payload.isIncreased);
break;
}
};

private changeFontSize = (isIncreased: boolean): void => {
const offset = isIncreased ? 2 : -2;
const currentFontSize = this.fontSizeMutableFlow.value!;
const newFontSize = currentFontSize + offset;
const coercedFontSize = Math.max(13, Math.min(25, newFontSize));
this.fontSizeMutableFlow.value = coercedFontSize;

this.settingDocument.set(StoreKeys.FONT_SIZE, coercedFontSize.toString());
};
}
45 changes: 0 additions & 45 deletions monosketch-svelte/src/lib/mono/ui-state-manager/index.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Flow } from 'lib/libs/flow';

export class PanelVisibilityManager {
private _shapeFormatPanelVisibilityFlow = new Flow<boolean>(true);
shapeFormatPanelVisibilityFlow = this._shapeFormatPanelVisibilityFlow.immutable();
private shapeFormatPanelVisibilityMutableFlow = new Flow<boolean>(true);
shapeFormatPanelVisibilityFlow = this.shapeFormatPanelVisibilityMutableFlow.immutable();

toggleShapeFormatPanelVisibility(): void {
this._shapeFormatPanelVisibilityFlow.value = !this._shapeFormatPanelVisibilityFlow.value;
setShapeFormatPanelVisibility(isVisible: boolean): void {
this.shapeFormatPanelVisibilityMutableFlow.value = isVisible;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,9 @@ export class AppThemeManager {
this.themeManager.setTheme(themeMode);
};

observeTheme = (appLifecycleOwner: LifecycleOwner, onThemeChange: () => void): void => {
observeTheme = (appLifecycleOwner: LifecycleOwner): void => {
this.themeManager.themeModeFlow.observe(appLifecycleOwner, (themeMode) => {
document.documentElement.className = themeMode;
onThemeChange();

this.settingsDocument.set(StoreKeys.THEME_MODE, themeMode);
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Copyright (c) 2024, tuanchauict
*/

import { type ScrollMode, ThemeMode } from "$mono/ui-state-manager/states";

export type UiStatePayloadType =
| { type: "ShapeToolVisibility", isVisible: boolean }
| { type: "ChangeScrollMode", scrollMode: ScrollMode }
| { type: "ChangeTheme", themeMode: ThemeMode }
| { type: "ChangeFontSize", isIncreased: boolean }

export const UiStatePayload = {
ShapeToolVisibility: (isVisible: boolean): UiStatePayloadType => ({ type: "ShapeToolVisibility", isVisible }),
ChangeScrollMode: (scrollMode: ScrollMode): UiStatePayloadType => ({ type: "ChangeScrollMode", scrollMode }),
ChangeTheme: (themeMode: ThemeMode): UiStatePayloadType => ({ type: "ChangeTheme", themeMode }),
ChangeFontSize: (isIncreased: boolean): UiStatePayloadType => ({ type: "ChangeFontSize", isIncreased }),
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { AppUiStateManager } from "$mono/ui-state-manager/app-ui-state-manager";
import { DrawingInfo } from '$mono/workspace/drawing-info';
import type { ThemeManager } from '$mono/ui-state-manager/theme-manager';
import { Size } from '$libs/graphics-geo/size';
import { DEFAULT_FONT } from '$mono/workspace/drawing-info/drawing-info-controller';
import { ThemeColors } from '$mono/ui-state-manager/states';
Expand All @@ -12,7 +12,7 @@ const AXIS_X_HEIGHT = 18.0;
export class AxisCanvasViewController extends BaseCanvasViewController {
constructor(
canvas: HTMLCanvasElement,
private themeManager: ThemeManager,
private appUiStateManager: AppUiStateManager,
) {
super(canvas);
}
Expand All @@ -39,7 +39,7 @@ export class AxisCanvasViewController extends BaseCanvasViewController {

private drawAxis = () => {
const context = this.context;
const themeManager = this.themeManager;
const themeManager = this.appUiStateManager;
const drawingInfo = this.drawingInfo;

const cellSizePx = this.drawingInfo.cellSizePx;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { AppUiStateManager } from "$mono/ui-state-manager/app-ui-state-manager";
import { BaseCanvasViewController } from '$mono/workspace/canvas/base-canvas-controller';
import { type ThemeManager } from '$mono/ui-state-manager/theme-manager';
import { HighlightType, type Pixel } from '$mono/monobitmap/board/pixel';
import { ThemeColors } from '$mono/ui-state-manager/states';
import { type ThemeColor, ThemeColors } from '$mono/ui-state-manager/states';
import type { MonoBoard } from '$mono/monobitmap/board/board';

/**
Expand All @@ -12,7 +12,7 @@ export class BoardCanvasViewController extends BaseCanvasViewController {
constructor(
canvas: HTMLCanvasElement,
private board: MonoBoard,
private themeManager: ThemeManager,
private appUiStateManager: AppUiStateManager,
) {
super(canvas);
}
Expand All @@ -31,22 +31,15 @@ export class BoardCanvasViewController extends BaseCanvasViewController {
if (pixel.isTransparent) {
return;
}
this.context.fillStyle = this.themeManager.getThemedColorCode(this.getPixelColor(pixel));
const pixelColor = HighlightTypeToThemeColor[pixel.highlight];
this.context.fillStyle = this.appUiStateManager.getThemedColorCode(pixelColor);
this.drawText(pixel.visualChar, row, column);
};

private getPixelColor = (pixel: Pixel) => {
switch (pixel.highlight) {
case HighlightType.NO:
return ThemeColors.Shape;
case HighlightType.SELECTED:
return ThemeColors.ShapeSelected;
case HighlightType.TEXT_EDITING:
return ThemeColors.ShapeTextEditing;
case HighlightType.LINE_CONNECT_FOCUSING:
return ThemeColors.ShapeLineConnectTarget;
default:
throw new Error(`Unknown highlight type: ${pixel.highlight}`);
}
};
}

const HighlightTypeToThemeColor: Record<HighlightType, ThemeColor> = {
[HighlightType.NO]: ThemeColors.Shape,
[HighlightType.SELECTED]: ThemeColors.ShapeSelected,
[HighlightType.TEXT_EDITING]: ThemeColors.ShapeTextEditing,
[HighlightType.LINE_CONNECT_FOCUSING]: ThemeColors.ShapeLineConnectTarget,
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { AppUiStateManager } from "$mono/ui-state-manager/app-ui-state-manager";
import { BaseCanvasViewController } from '$mono/workspace/canvas/base-canvas-controller';
import type { ThemeManager } from '$mono/ui-state-manager/theme-manager';
import { ThemeColors } from '$mono/ui-state-manager/states';

/**
Expand All @@ -8,14 +8,14 @@ import { ThemeColors } from '$mono/ui-state-manager/states';
export class GridCanvasViewController extends BaseCanvasViewController {
constructor(
canvas: HTMLCanvasElement,
private themeManager: ThemeManager,
private appUiStateManager: AppUiStateManager,
) {
super(canvas);
}

protected drawInternal = () => {
const context = this.context;
context.strokeStyle = this.themeManager.getThemedColorCode(ThemeColors.GridLine);
context.strokeStyle = this.appUiStateManager.getThemedColorCode(ThemeColors.GridLine);
context.lineWidth = 0.25;
context.stroke(this.createGridPath());
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Point } from "$libs/graphics-geo/point";
import { TODO } from "$libs/todo";
import type { AppUiStateManager } from "$mono/ui-state-manager/app-ui-state-manager";
import { BaseCanvasViewController } from '$mono/workspace/canvas/base-canvas-controller';
import type { ThemeManager } from '$mono/ui-state-manager/theme-manager';
import {
type InteractionBound,
InteractionBoundType,
Expand All @@ -26,7 +26,7 @@ export class InteractionCanvasViewController extends BaseCanvasViewController {

constructor(
canvas: HTMLCanvasElement,
private themeManager: ThemeManager,
private appUiStateManager: AppUiStateManager,
) {
super(canvas);
this.context.imageSmoothingQuality = 'high';
Expand Down Expand Up @@ -76,7 +76,7 @@ export class InteractionCanvasViewController extends BaseCanvasViewController {

private drawScalableInteractionBound = (bound: ScalableInteractionBound) => {
const context = this.context;
context.strokeStyle = this.themeManager.getThemedColorCode(
context.strokeStyle = this.appUiStateManager.getThemedColorCode(
ThemeColors.SelectionBoundStroke,
);
context.lineWidth = 1.0;
Expand Down Expand Up @@ -109,8 +109,8 @@ export class InteractionCanvasViewController extends BaseCanvasViewController {
dotPath.moveTo(xPx, yPx);
dotPath.arc(xPx, yPx, dotRadius, 0, FULL_CIRCLE_ARG);
}
context.strokeStyle = this.themeManager.getThemedColorCode(strokeColor);
context.fillStyle = this.themeManager.getThemedColorCode(fillColor);
context.strokeStyle = this.appUiStateManager.getThemedColorCode(strokeColor);
context.fillStyle = this.appUiStateManager.getThemedColorCode(fillColor);
context.imageSmoothingEnabled = true;
context.stroke(dotPath);
context.fill(dotPath);
Expand All @@ -129,7 +129,7 @@ export class InteractionCanvasViewController extends BaseCanvasViewController {
return path;
};

getInteractionPoint = (pointPx: Point): InteractionPoint | null => {
getInteractionPoint = (_pointPx: Point): InteractionPoint | null => {
// TODO: Implement this method
TODO("Implement this method");
return null;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { AppUiStateManager } from "$mono/ui-state-manager/app-ui-state-manager";
import { BaseCanvasViewController } from '$mono/workspace/canvas/base-canvas-controller';
import { type ThemeManager } from '$mono/ui-state-manager/theme-manager';
import type { Rect } from '$libs/graphics-geo/rect';
import { ThemeColors } from '$mono/ui-state-manager/states';

Expand All @@ -13,7 +13,7 @@ export class SelectionCanvasViewController extends BaseCanvasViewController {

constructor(
canvas: HTMLCanvasElement,
private themeManager: ThemeManager,
private appUiStateManager: AppUiStateManager,
) {
super(canvas);
}
Expand All @@ -29,7 +29,6 @@ export class SelectionCanvasViewController extends BaseCanvasViewController {
}

const context = this.context;
const themeManager = this.themeManager;
const drawingInfo = this.drawingInfo;

const leftPx = drawingInfo.toXPx(bound.left);
Expand All @@ -39,7 +38,7 @@ export class SelectionCanvasViewController extends BaseCanvasViewController {

const path = new Path2D();
path.rect(leftPx, topPx, rightPx - leftPx, bottomPx - topPx);
context.strokeStyle = themeManager.getThemedColorCode(ThemeColors.SelectionAreaStroke);
context.strokeStyle = this.appUiStateManager.getThemedColorCode(ThemeColors.SelectionAreaStroke);
context.lineWidth = 1.0;
context.setLineDash(DASH_PATTERN);
context.stroke(path);
Expand Down
Loading

0 comments on commit 59dc48a

Please sign in to comment.