Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add navigation+layout buttons for mobile #7178

Merged
merged 33 commits into from
Jul 18, 2023
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
57f2148
experiment with floating controls on mobile
philippotto Jun 23, 2023
3af267e
add functionality to floating buttons and detect touch interfaces aut…
philippotto Jun 26, 2023
693bf1c
fix typing
philippotto Jun 26, 2023
7b01ed6
automatically hide sidebars on narrow screens; increase size of buttons
philippotto Jun 26, 2023
1193824
temporarily disable most CI checks
philippotto Jun 26, 2023
0c4f7ea
experiment with keeping move button pressed
philippotto Jun 26, 2023
ed607f7
refactor
philippotto Jun 27, 2023
df051dc
tune screen width heuristic
philippotto Jun 27, 2023
d69a7cf
Revert "temporarily disable most CI checks"
philippotto Jun 27, 2023
e22b5cc
update changelog
philippotto Jun 27, 2023
a942ac7
make buttons even bigger and use different icon for maximize
philippotto Jun 27, 2023
d958886
adapt viewport meta tag to avoid auto-sizing; disable automatic clear…
philippotto Jun 27, 2023
262bb5a
automatic blur for floating buttons
philippotto Jun 27, 2023
f248587
Revert "Revert "temporarily disable most CI checks""
philippotto Jun 27, 2023
83e2718
tune button sizes again
philippotto Jun 27, 2023
384a2fd
tune size of brain spinner
philippotto Jun 27, 2023
5c51bb3
remove meta-viewport-tag clearing code completely
philippotto Jun 28, 2023
1c0e4be
add info-button for active viewport; disable navigation buttons when …
philippotto Jun 28, 2023
3b1eb13
Merge branch 'master' of github.com:scalableminds/webknossos into mob…
philippotto Jun 28, 2023
8caa4a0
clean up
philippotto Jun 28, 2023
5576988
move react hook into utils module
philippotto Jun 28, 2023
f8a11cc
Revert "Revert "Revert "temporarily disable most CI checks"""
philippotto Jun 28, 2023
b09ef39
make floating buttons unselectable and zoom to center during pinch
philippotto Jun 29, 2023
34a7255
Apply suggestions from code review
philippotto Jun 29, 2023
7cd32f5
fix navbar height on small screens
philippotto Jun 29, 2023
00f2d08
Merge branch 'mobile-tracing' of github.com:scalableminds/webknossos …
philippotto Jun 29, 2023
26feb23
remove 54px height rule for navbar on mobile
philippotto Jun 29, 2023
99b03dd
temporarily disable most CI checks
philippotto Jun 26, 2023
6f46db7
fix broken css property names
philippotto Jun 29, 2023
1a44a4a
move in right direction for arbitrary modes; make long presses use
philippotto Jun 30, 2023
dea8513
change user-scalable to no
philippotto Jun 30, 2023
8c39ced
restore ci checks
philippotto Jul 18, 2023
b91b1bb
Merge branch 'master' of github.com:scalableminds/webknossos into mob…
philippotto Jul 18, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .circleci/not-on-master.sh
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#!/usr/bin/env bash
set -Eeuo pipefail

if [ "${CIRCLE_BRANCH}" == "master" ]; then
# if [ "${CIRCLE_BRANCH}" == "master" ]; then
echo "Skipping this step on master..."
else
exec "$@"
fi
# else
# exec "$@"
# fi
1 change: 1 addition & 0 deletions CHANGELOG.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released

### Changed
- Redesigned the info tab in the right-hand sidebar to be fit the new branding and design language. [#7110](https://github.com/scalableminds/webknossos/pull/7110)
- Improved support for touch devices by adding floating buttons for easier navigation and layout changes. [#7178](https://github.com/scalableminds/webknossos/pull/7178)
- Optimized processing of parallel requests (new thread pool configuration, asynchronous FossilDB client), improving performance and reducing idle waiting. [#7139](https://github.com/scalableminds/webknossos/pull/7139)

### Fixed
Expand Down
21 changes: 16 additions & 5 deletions app/views/main.scala.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1, maximum-scale=1, user-scalable=yes"
/>
<meta name="commit-hash" content="@(webknossos.BuildInfo.commitHash)" />
<title>@(conf.WebKnossos.tabTitle)</title>
@if(conf.Features.isWkorgInstance){
Expand Down Expand Up @@ -67,12 +70,20 @@
<main id="main-container"></main>

@if(conf.GoogleAnalytics.trackingId.nonEmpty) {
<script async src="https://www.googletagmanager.com/gtag/js?id=@(conf.GoogleAnalytics.trackingId)"></script>
<script
async
src="https://www.googletagmanager.com/gtag/js?id=@(conf.GoogleAnalytics.trackingId)"
></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() { dataLayer.push(arguments); }
gtag('js', new Date());
gtag('config', '@(conf.GoogleAnalytics.trackingId)', { 'anonymize_ip': true, 'cookie_expires': 0 });
function gtag() {
dataLayer.push(arguments);
}
gtag("js", new Date());
gtag("config", "@(conf.GoogleAnalytics.trackingId)", {
anonymize_ip: true,
cookie_expires: 0,
});
</script>
}
</body>
Expand Down
13 changes: 0 additions & 13 deletions frontend/javascripts/components/adapt_viewport_metatag.ts

This file was deleted.

4 changes: 2 additions & 2 deletions frontend/javascripts/components/brain_spinner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ export default function BrainSpinner({ message, isLoading = true }: Props) {
src="/assets/images/brain.svg"
alt=""
style={{
width: 375,
height: 299,
width: "100%",
height: "100%",
marginLeft: "auto",
marginRight: "auto",
marginTop: "10%",
Expand Down
2 changes: 1 addition & 1 deletion frontend/javascripts/libs/input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -617,7 +617,7 @@ export class InputMouse {
if (this.lastScale != null) {
const delta = evt.scale - this.lastScale;
this.lastScale = evt.scale;
this.emitter.emit("pinch", 10 * delta);
this.emitter.emit("pinch", 10 * delta, this.position);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In Slack you mentioned that this 10 * possibly should be removed

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant that the factor 10 feels fishy, but I don't think that it can be simply removed. I suspect that it's there for a reason 🤔 It was added in this PR: https://github.com/scalableminds/webknossos/pull/2070/files#diff-f1af7da1cff58c6ece76e97bdc89036a204b0e0f4ce57a201f5ff8a1b365ef43R455. Since I cannot really test the pinch-to-zoom feature, I'd leave it as is for now..

}
};

Expand Down
33 changes: 33 additions & 0 deletions frontend/javascripts/libs/react_hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,39 @@ export function useKeyPress(targetKey: "Shift" | "Alt" | "Control") {
return keyPressed;
}

export function useRepeatedButtonTrigger(triggerCallback: () => void, repeatDelay: number = 150) {
const [isPressed, setIsPressed] = useState(false);

useEffect(() => {
let timerId: NodeJS.Timeout;

if (isPressed) {
const trigger = () => {
timerId = setTimeout(() => {
triggerCallback();
trigger();
}, repeatDelay);
};

trigger();
}

return () => {
clearTimeout(timerId);
};
}, [isPressed, triggerCallback]);

const onTouchStart = () => {
setIsPressed(true);
};

const onTouchEnd = () => {
setIsPressed(false);
};

return { onClick: triggerCallback, onTouchStart, onTouchEnd };
}

export function useSearchParams() {
const location = useLocation();
return Object.fromEntries(new URLSearchParams(location.search).entries());
Expand Down
14 changes: 4 additions & 10 deletions frontend/javascripts/libs/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ export function mod(x: number, n: number) {
return ((x % n) + n) % n;
}

export function keys<T extends string | number | symbol>(o: Record<T, any>): T[] {
return Object.keys(o) as Array<keyof typeof o>;
}

export function values<T>(o: { [s: string]: T } | ArrayLike<T>): T[] {
return Object.values(o);
}
Expand Down Expand Up @@ -1070,16 +1074,6 @@ export function getWindowBounds(): [number, number] {
return [width, height];
}

export function disableViewportMetatag() {
const viewport = document.querySelector("meta[name=viewport]");

if (!viewport) {
return;
}

viewport.setAttribute("content", "");
}

/**
* Deep diff between two object, using lodash
* @param {Object} object Object compared
Expand Down
12 changes: 0 additions & 12 deletions frontend/javascripts/navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -739,17 +739,6 @@ function Navbar({
isHelpMenuOpen,
);

const navbarStyle: Record<string, any> = {
padding: 0,
overflowX: "auto",
overflowY: "hidden",
position: "fixed",
height: navbarHeight,
display: "flex",
alignItems: "center",
whiteSpace: "nowrap",
};

const _isAuthenticated = isAuthenticated && activeUser != null;

const isAdminOrManager = activeUser != null ? Utils.isUserAdminOrManager(activeUser) : false;
Expand Down Expand Up @@ -826,7 +815,6 @@ function Navbar({

return (
<Header
style={navbarStyle}
className={classnames("navbar-header", {
"collapsed-nav-header": collapseAllNavItems,
})}
Expand Down
2 changes: 1 addition & 1 deletion frontend/javascripts/oxalis/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export enum OrthoViews {
PLANE_XZ = "PLANE_XZ",
TDView = "TDView",
}
export const enum OrthoViewsToName {
export enum OrthoViewsToName {
PLANE_XY = "XY",
PLANE_YZ = "YZ",
PLANE_XZ = "XZ",
Expand Down
3 changes: 0 additions & 3 deletions frontend/javascripts/oxalis/controller.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,6 @@ class Controller extends React.PureComponent<PropsWithRouter, State> {
// controller - a controller for each row, each column and each
// cross in this matrix.
componentDidMount() {
// The annotation view should be rendered without the special mobile-friendly
// viewport meta tag.
Utils.disableViewportMetatag();
this._isMounted = true;
Store.dispatch(setIsInAnnotationViewAction(true));
UrlManager.initialize();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,10 @@ export class MoveTool {

handleClickSegment(pos);
},
pinch: (delta: number) => MoveHandlers.zoom(delta, true),
pinch: (delta: number, center: Point2) => {
MoveHandlers.setMousePosition(center);
MoveHandlers.zoom(delta, true);
},
mouseMove: MoveHandlers.moveWhenAltIsPressed,
out: () => {
MoveHandlers.setMousePosition(null);
Expand Down
28 changes: 18 additions & 10 deletions frontend/javascripts/oxalis/model/actions/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,22 @@ export type Action =
| SegmentationAction
| ConnectomeAction
| ProofreadAction
| OrganizationAction;
| OrganizationAction
| ReturnType<typeof wkReadyAction>
| ReturnType<typeof sceneControllerReadyAction>
| ReturnType<typeof restartSagaAction>;

export const wkReadyAction = () => ({
type: "WK_READY",
});
export const sceneControllerReadyAction = () => ({
type: "SCENE_CONTROLLER_READY",
});
export const restartSagaAction = () => ({
type: "RESTART_SAGA",
});
export const wkReadyAction = () =>
({
type: "WK_READY",
} as const);

export const sceneControllerReadyAction = () =>
({
type: "SCENE_CONTROLLER_READY",
} as const);

export const restartSagaAction = () =>
({
type: "RESTART_SAGA",
} as const);
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,15 @@ function BorderToggleButton(props: Props) {
className={className}
size="small"
/*
Normally, a simple onClick handler would be enough for this button
to support both desktops as mobile devices with touchscreens.
However, since the button is placed in the FlexLayout's panels,
its presence interferes with the existing mouse drag / touch drag
behavior to move tabs around.
For this reason, we have to call stopPropagation and preventDefault.
Additionally, we need to detect whether the user has dragged a tab
across screen (to move a tab). In that case, onTouchEnd does nothing.
*/
Normally, a simple onClick handler would be enough for this button
to support both desktops as mobile devices with touchscreens.
However, since the button is placed in the FlexLayout's panels,
its presence interferes with the existing mouse drag / touch drag
behavior to move tabs around.
For this reason, we have to call stopPropagation and preventDefault.
Additionally, we need to detect whether the user has dragged a tab
across screen (to move a tab). In that case, onTouchEnd does nothing.
*/
onClick={(event) => {
if (event != null) {
// @ts-expect-error ts-migrate(2339) FIXME: Property 'blur' does not exist on type 'EventTarge... Remove this comment to see the full error message
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,20 +112,14 @@ const borderTabs: Record<keyof typeof BorderTabs, TabNode> = {};
Utils.entries(BorderTabs).forEach(([tabKey, borderTab]: [string, BorderTabType]) => {
borderTabs[tabKey] = getTabDescriptorForBorderTab(borderTab);
});
// @ts-expect-error ts-migrate(2739) FIXME: Type '{}' is missing the following properties from... Remove this comment to see the full error message
const OrthoViewports: Record<keyof typeof OrthoViews, TabNode> = {};
Object.keys(OrthoViews).forEach((viewportId) => {
// @ts-expect-error ts-migrate(2476) FIXME: A const enum member can only be accessed using a s... Remove this comment to see the full error message
const OrthoViewports = {} as Record<keyof typeof OrthoViews, TabNode>;
Utils.keys(OrthoViews).forEach((viewportId) => {
const name = OrthoViewsToName[viewportId];
// @ts-expect-error ts-migrate(7053) FIXME: Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
OrthoViewports[viewportId] = Tab(name, viewportId, "viewport");
});
// @ts-expect-error ts-migrate(2739) FIXME: Type '{}' is missing the following properties from... Remove this comment to see the full error message
const ArbitraryViewports: Record<keyof typeof ArbitraryViews, TabNode> = {};
Object.keys(ArbitraryViews).forEach((viewportId) => {
// @ts-expect-error ts-migrate(7053) FIXME: Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
const ArbitraryViewports = {} as Record<keyof typeof ArbitraryViews, TabNode>;
Utils.keys(ArbitraryViews).forEach((viewportId) => {
const name = ArbitraryViewsToName[viewportId];
// @ts-expect-error ts-migrate(7053) FIXME: Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
ArbitraryViewports[viewportId] = Tab(name, viewportId, "viewport");
});
const globalLayoutSettings: GlobalConfig = {
Expand All @@ -149,8 +143,12 @@ function buildTabsets(setsOfTabs: Array<Array<TabNode>>): Array<TabsetNode> {
return tabsets;
}

// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'side' implicitly has an 'any' type.
function buildBorder(side, tabset: Array<TabNode>, width: number, isBorderOpen: boolean): Border {
function buildBorder(
side: "left" | "right",
tabset: Array<TabNode>,
width: number,
isBorderOpen: boolean,
): Border {
const buildTabset = Tabset(tabset, 100);
const border: Border = {
type: "border",
Expand Down
31 changes: 19 additions & 12 deletions frontend/javascripts/oxalis/view/layouting/flex_layout_wrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ import {
} from "./flex_layout_helper";
import { layoutEmitter, getLayoutConfig } from "./layout_persistence";
import BorderToggleButton from "../components/border_toggle_button";

const { Footer } = Layout;

type Model = InstanceType<typeof FlexLayout.Model>;
type Action = InstanceType<typeof FlexLayout.Action>;
type StateProps = {
Expand Down Expand Up @@ -124,6 +126,11 @@ class FlexLayoutWrapper extends React.PureComponent<Props, State> {
this.toggleBorder(side);
}),
);
this.unbindListeners.push(
layoutEmitter.on("toggleMaximize", () => {
this.toggleMaximize();
}),
);
this.unbindListeners.push(this.attachKeyboardShortcuts());
}

Expand Down Expand Up @@ -210,23 +217,23 @@ class FlexLayoutWrapper extends React.PureComponent<Props, State> {
}
}

attachKeyboardShortcuts() {
const toggleMaximize = () => {
const { model } = this.state;
const activeNode = model.getActiveTabset();
toggleMaximize = () => {
const { model } = this.state;
const activeNode = model.getActiveTabset();

if (activeNode == null) {
return;
}
if (activeNode == null) {
return;
}

const toggleMaximiseAction = FlexLayout.Actions.maximizeToggle(activeNode.getId());
model.doAction(toggleMaximiseAction);
this.onAction(toggleMaximiseAction);
};
const toggleMaximiseAction = FlexLayout.Actions.maximizeToggle(activeNode.getId());
model.doAction(toggleMaximiseAction);
this.onAction(toggleMaximiseAction);
};

attachKeyboardShortcuts() {
const keyboardNoLoop = new InputKeyboardNoLoop(
{
".": toggleMaximize,
".": this.toggleMaximize,
k: () => this.toggleBorder("left"),
l: () => this.toggleBorder("right"),
},
Expand Down
Loading