Skip to content

Commit

Permalink
Add fullscreen button
Browse files Browse the repository at this point in the history
The icon doesn't change because the component is never informed of the
change, but this is trivial. I just wanted to get this feature out
because it's something I've been desiring for a while now.

On Android, fullscreen doesn't mean "fullscreen". It just takes up 100%
of the app's viewport, rather than fullscreening like a video player
would. I'd like to work out what to do to achieve true fullscreen but in
the interest of time, here's a quick fullscreen solution.
  • Loading branch information
J-Cake committed Nov 22, 2024
1 parent 0e1ff8d commit 958cfda
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 114 deletions.
189 changes: 101 additions & 88 deletions src/tldraw/drawing-menu/drawing-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { RedoIcon } from "src/graphics/icons/redo-icon";
import { SelectIcon } from "src/graphics/icons/select-icon";
import { EraseIcon } from "src/graphics/icons/erase-icon";
import { DrawIcon } from "src/graphics/icons/draw-icon";
import { FullscreenIcon } from "src/graphics/icons/fullscreen-icon";
import { ExitFullscreenIcon } from "src/graphics/icons/exit-fullscreen-icon";
import { Editor } from "@tldraw/tldraw";
import { silentlyChangeStore } from "src/utils/tldraw-helpers";

Expand All @@ -17,56 +19,57 @@ export enum tool {
eraser = 'eraser',
}
interface DrawingMenuProps {
getTlEditor: () => Editor | undefined,
onStoreChange: (elEditor: Editor) => void,
getTlEditor: () => Editor | undefined,
onStoreChange: (elEditor: Editor) => void,
sendSignal: (signal: string) => void
}

export const DrawingMenu = React.forwardRef<HTMLDivElement, DrawingMenuProps>((props, ref) => {

const [curTool, setCurTool] = React.useState<tool>(tool.draw);
const [curTool, setCurTool] = React.useState<tool>(tool.draw);
const [canUndo, setCanUndo] = React.useState<boolean>(false);
const [canRedo, setCanRedo] = React.useState<boolean>(false);

React.useEffect( () => {
console.log('MENUBAR MOUNTED');
let removeUserActionListener: () => void;
const mountDelayMs = 100;
setTimeout( () => {
const tlEditor = props.getTlEditor();
if(!tlEditor) return;

let timeout: NodeJS.Timeout;
removeUserActionListener = tlEditor.store.listen((entry) => {
clearTimeout(timeout);
timeout = setTimeout( () => { // TODO: Create a debounce helper
setCanUndo( tlEditor.getCanUndo() );
setCanRedo( tlEditor.getCanRedo() );
}, 100);
}, {
source: 'all',
scope: 'all' // Filters some things like camera movement changes. But Not sure it's locked down enough, so leaving as all.
})
}, mountDelayMs);

return () => removeUserActionListener();
}, []);

///////////

function undo() {
React.useEffect(() => {
console.log('MENUBAR MOUNTED');

let removeUserActionListener: () => void;

const mountDelayMs = 100;
setTimeout(() => {
const tlEditor = props.getTlEditor();
if (!tlEditor) return;

let timeout: NodeJS.Timeout;
removeUserActionListener = tlEditor.store.listen((entry) => {
clearTimeout(timeout);
timeout = setTimeout(() => { // TODO: Create a debounce helper
setCanUndo(tlEditor.getCanUndo());
setCanRedo(tlEditor.getCanRedo());
}, 100);
}, {
source: 'all',
scope: 'all' // Filters some things like camera movement changes. But Not sure it's locked down enough, so leaving as all.
})
}, mountDelayMs);

return () => removeUserActionListener();
}, []);

///////////

function undo() {
const editor = props.getTlEditor();
if (!editor) return;
silentlyChangeStore( editor, () => {
silentlyChangeStore(editor, () => {
editor.undo();
});
props.onStoreChange(editor)
}
function redo() {
const editor = props.getTlEditor();
if (!editor) return;
silentlyChangeStore( editor, () => {
silentlyChangeStore(editor, () => {
editor.redo();
});
props.onStoreChange(editor)
Expand All @@ -91,61 +94,71 @@ export const DrawingMenu = React.forwardRef<HTMLDivElement, DrawingMenuProps>((p
editor.setCurrentTool('eraser');
setCurTool(tool.eraser);
}
function activateFullscreen() {
const editor = props.getTlEditor();
if (!editor || !props.sendSignal) return;
props.sendSignal("toggle-fullscreen");
}

///////////
///////////

return <>
<div
ref={ref}
className='ink_menu-bar'
>
<div
className='ink_quick-menu'
>
<button
onPointerDown={undo}
disabled={!canUndo}
>
<UndoIcon />
</button>
<button
onPointerDown={redo}
disabled={!canRedo}
>
<RedoIcon />
</button>
</div>
<div
className='ink_tool-menu'
>
<button
onPointerDown={activateSelectTool}
disabled={curTool === tool.select}
>
<SelectIcon />
</button>
<button
onPointerDown={activateDrawTool}
disabled={curTool === tool.draw}
>
<DrawIcon />
</button>
<button
onPointerDown={activateEraseTool}
disabled={curTool === tool.eraser}
>
<EraseIcon />
</button>
</div>
<div
className='ink_other-menu'
>
<button
onPointerDown={activateFullscreen}
>
{document.fullscreenElement ? <ExitFullscreenIcon /> : <FullscreenIcon />}

///////////
///////////

return <>
<div
ref = {ref}
className = 'ink_menu-bar'
>
<div
className='ink_quick-menu'
>
<button
onPointerDown={undo}
disabled={!canUndo}
>
<UndoIcon/>
</button>
<button
onPointerDown={redo}
disabled={!canRedo}
>
<RedoIcon/>
</button>
</div>
<div
className='ink_tool-menu'
>
<button
onPointerDown={activateSelectTool}
disabled={curTool === tool.select}
>
<SelectIcon/>
</button>
<button
onPointerDown={activateDrawTool}
disabled={curTool === tool.draw}
>
<DrawIcon/>
</button>
<button
onPointerDown={activateEraseTool}
disabled={curTool === tool.eraser}
>
<EraseIcon/>
</button>
</div>
<div
className='ink_other-menu'
>

</div>
</div>
</>;
</button>
</div>
</div>
</>;

});

export default DrawingMenu;
export default DrawingMenu;
65 changes: 39 additions & 26 deletions src/tldraw/drawing/tldraw-drawing-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export function TldrawDrawingEditor(props: {
const tldrawContainerElRef = useRef<HTMLDivElement>(null);
const tlEditorRef = useRef<Editor>();
const [tlStoreSnapshot] = React.useState<StoreSnapshot<TLRecord>>(prepareDrawingSnapshot(props.pageData.tldraw))

const handleMount = (_editor: Editor) => {
const editor = tlEditorRef.current = _editor;

Expand All @@ -66,7 +66,7 @@ export function TldrawDrawingEditor(props: {
editor.updateInstanceState({
isGridMode: true,
})

// view setup
initDrawingCamera(editor);
if (props.embedded) {
Expand Down Expand Up @@ -109,8 +109,8 @@ export function TldrawDrawingEditor(props: {
default:
// Catch anything else not specifically mentioned (ie. erase, draw shape, etc.)
queueOrRunStorePostProcesses(editor);
// console.log('Activity not recognised.');
// console.log('entry', JSON.parse(JSON.stringify(entry)) );
// console.log('Activity not recognised.');
// console.log('entry', JSON.parse(JSON.stringify(entry)) );
}

}, {
Expand All @@ -124,7 +124,7 @@ export function TldrawDrawingEditor(props: {
removeUserActionListener();
}

if(props.registerControls) {
if (props.registerControls) {
props.registerControls({
save: () => completeSave(editor),
saveAndHalt: async (): Promise<void> => {
Expand All @@ -133,8 +133,8 @@ export function TldrawDrawingEditor(props: {
},
})
}
if(props.onReady) props.onReady();

if (props.onReady) props.onReady();

return () => {
unmountActions();
Expand Down Expand Up @@ -218,8 +218,8 @@ export function TldrawDrawingEditor(props: {
previewUri = svgObj.svg;//await svgToPngDataUri(svgObj)
// if(previewUri) addDataURIImage(previewUri) // NOTE: Option for testing
}
if(previewUri) {

if (previewUri) {
const pageData = buildDrawingFileData({
tlStoreSnapshot,
previewUri,
Expand All @@ -241,50 +241,63 @@ export function TldrawDrawingEditor(props: {
return tlEditorRef.current;
};

function handleSignal(signal: string) {
switch (signal) {
case "toggle-fullscreen":
if (tldrawContainerElRef.current)
if (document.fullscreenElement)
document.exitFullscreen();
else
tldrawContainerElRef.current.requestFullscreen();
break;
}
}

//////////////

return <>
<div
ref = {tldrawContainerElRef}
className = {classNames([
ref={tldrawContainerElRef}
className={classNames([
"ddc_ink_drawing-editor"
])}
style = {{
style={{
height: '100%',
position: 'relative'
}}
>
<TldrawEditor
options = {tlOptions}
shapeUtils = {[...defaultShapeUtils]}
tools = {[...defaultTools, ...defaultShapeTools]}
initialState = "draw"
snapshot = {tlStoreSnapshot}
options={tlOptions}
shapeUtils={[...defaultShapeUtils]}
tools={[...defaultTools, ...defaultShapeTools]}
initialState="draw"
snapshot={tlStoreSnapshot}
// persistenceKey = {props.fileRef.path}

// bindingUtils = {defaultBindingUtils}
components = {defaultComponents}
components={defaultComponents}

onMount = {handleMount}
onMount={handleMount}

// Ensure cursor remains and embed IS NOT focussed if it's an embed.
// This prevents tldraw scrolling the page to the top of the embed when turning on.
// But a side effect of false is preventing mousewheel scrolling and zooming.
autoFocus = {props.embedded ? false : true}
autoFocus={props.embedded ? false : true}
/>

<PrimaryMenuBar>
<DrawingMenu
getTlEditor = {getTlEditor}
onStoreChange = {(tlEditor: Editor) => queueOrRunStorePostProcesses(tlEditor)}
getTlEditor={getTlEditor}
onStoreChange={(tlEditor: Editor) => queueOrRunStorePostProcesses(tlEditor)}
sendSignal={(signal: string) => handleSignal(signal)}
/>
{props.embedded && props.commonExtendedOptions && (
<ExtendedDrawingMenu
onLockClick = { async () => {
onLockClick={async () => {
// TODO: Save immediately incase it hasn't been saved yet?
if(props.closeEditor) props.closeEditor();
if (props.closeEditor) props.closeEditor();
}}
menuOptions = {props.commonExtendedOptions}
menuOptions={props.commonExtendedOptions}
/>
)}
</PrimaryMenuBar>
Expand Down

0 comments on commit 958cfda

Please sign in to comment.