diff --git a/CHANGELOG.unreleased.md b/CHANGELOG.unreleased.md index 614c54aa726..c4f2d586c5c 100644 --- a/CHANGELOG.unreleased.md +++ b/CHANGELOG.unreleased.md @@ -25,6 +25,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released - Improved the default colors for skeleton trees. [#8228](https://github.com/scalableminds/webknossos/pull/8228) - Allowed to train an AI model using differently sized bounding boxes. We recommend all bounding boxes to have equal dimensions or to have dimensions which are multiples of the smallest bounding box. [#8222](https://github.com/scalableminds/webknossos/pull/8222) - Within the bounding box tool, the cursor updates immediately after pressing `ctrl`, indicating that a bounding box can be moved instead of resized. [#8253](https://github.com/scalableminds/webknossos/pull/8253) +- Improved the styling of active tools and modes in the toolbar. [#8295](https://github.com/scalableminds/webknossos/pull/8295) ### Fixed - Fixed that listing datasets with the `api/datasets` route without compression failed due to missing permissions regarding public datasets. [#8249](https://github.com/scalableminds/webknossos/pull/8249) diff --git a/frontend/javascripts/components/fast_tooltip.tsx b/frontend/javascripts/components/fast_tooltip.tsx index cb31751b8fc..d80d2ddd49c 100644 --- a/frontend/javascripts/components/fast_tooltip.tsx +++ b/frontend/javascripts/components/fast_tooltip.tsx @@ -32,7 +32,7 @@ import { Tooltip as ReactTooltip } from "react-tooltip"; const ROOT_TOOLTIP_IDS = { DEFAULT: "main-tooltip", DYNAMIC: "main-tooltip-dynamic", -}; +} as const; export type FastTooltipPlacement = | "top" diff --git a/frontend/javascripts/navbar.tsx b/frontend/javascripts/navbar.tsx index 12873f12c91..c3169385ea9 100644 --- a/frontend/javascripts/navbar.tsx +++ b/frontend/javascripts/navbar.tsx @@ -983,16 +983,17 @@ function Navbar({ paddingTop: navbarHeight > constants.DEFAULT_NAVBAR_HEIGHT ? constants.BANNER_HEIGHT : 0, }} /> - -
- {trailingNavItems} -
+ +
+ {trailingNavItems} +
+
); } diff --git a/frontend/javascripts/oxalis/view/action-bar/toolbar_view.tsx b/frontend/javascripts/oxalis/view/action-bar/toolbar_view.tsx index a48140cc1b6..000fe1f5ca3 100644 --- a/frontend/javascripts/oxalis/view/action-bar/toolbar_view.tsx +++ b/frontend/javascripts/oxalis/view/action-bar/toolbar_view.tsx @@ -52,7 +52,7 @@ import { setToolAction, showQuickSelectSettingsAction } from "oxalis/model/actio import { updateUserSettingAction } from "oxalis/model/actions/settings_actions"; import { usePrevious, useKeyPress } from "libs/react_hooks"; import { userSettings } from "types/schemas/user_settings.schema"; -import ButtonComponent from "oxalis/view/components/button_component"; +import ButtonComponent, { ToggleButton } from "oxalis/view/components/button_component"; import { MaterializeVolumeAnnotationModal } from "oxalis/view/action-bar/starting_job_modals"; import { ToolsWithOverwriteCapabilities, @@ -91,12 +91,6 @@ const NARROW_BUTTON_STYLE = { paddingLeft: 10, paddingRight: 8, }; -// The z-index is needed so that the blue border of an active button does override the border color of the neighboring non active button. -const ACTIVE_BUTTON_STYLE = { - ...NARROW_BUTTON_STYLE, - borderColor: "var(--ant-color-primary)", - zIndex: 1, -}; const imgStyleForSpaceyIcons = { width: 19, height: 19, @@ -173,7 +167,7 @@ function RadioButtonWithTooltip({ disabledTitle?: string; disabled?: boolean; children: React.ReactNode; - style: React.CSSProperties; + style?: React.CSSProperties; value: string; onClick?: (event: React.MouseEvent) => void; onMouseEnter?: () => void; @@ -219,7 +213,7 @@ function ToolRadioButton({ disabledExplanation?: string; disabled?: boolean; children: React.ReactNode; - style: React.CSSProperties; + style?: React.CSSProperties; value: string; onClick?: (event: React.MouseEvent) => void; onMouseEnter?: () => void; @@ -297,7 +291,6 @@ function OverwriteModeSwitch({ > state.temporaryConfiguration.isMergerModeEnabled, @@ -420,21 +412,22 @@ function AdditionalSkeletonModesButtons() { const toggleMergerMode = () => dispatch(setMergerModeEnabledAction(!isMergerModeEnabled)); - const newNodeNewTreeModeButtonStyle = isNewNodeNewTreeModeOn - ? ACTIVE_BUTTON_STYLE - : NARROW_BUTTON_STYLE; - const mergerModeButtonStyle = isMergerModeEnabled ? ACTIVE_BUTTON_STYLE : NARROW_BUTTON_STYLE; - const isMaterializeVolumeAnnotationEnabled = dataset.dataStore.jobsSupportedByAvailableWorkers.includes( APIJobType.MATERIALIZE_VOLUME_ANNOTATION, ); return ( - - + + Single Node Tree Mode - - + - + {isMergerModeEnabled && isMaterializeVolumeAnnotationEnabled && isUserAdminOrManager && ( setShowMaterializeVolumeAnnotationModal(false)} /> )} - + ); } @@ -940,7 +934,6 @@ export default function ToolbarView() { description="Use left-click to move around and right-click to open a context menu." disabledExplanation="" disabled={false} - style={NARROW_BUTTON_STYLE} value={AnnotationToolEnum.MOVE} > @@ -952,7 +945,6 @@ export default function ToolbarView() { description={skeletonToolDescription} disabledExplanation={disabledInfosForTools[AnnotationToolEnum.SKELETON].explanation} disabled={disabledInfosForTools[AnnotationToolEnum.SKELETON].isDisabled} - style={NARROW_BUTTON_STYLE} value={AnnotationToolEnum.SKELETON} > + + Quick Select Icon + ) : null} - - Quick Select Icon - { dispatch(ensureLayerMappingsAreLoadedAction()); @@ -1154,6 +1141,7 @@ export default function ToolbarView() { className="fas fa-clipboard-check" style={{ opacity: disabledInfosForTools[AnnotationToolEnum.PROOFREAD].isDisabled ? 0.5 : 1, + padding: "0 4px", }} /> @@ -1163,7 +1151,6 @@ export default function ToolbarView() { description="Use to measure distances or areas." disabledExplanation="" disabled={false} - style={NARROW_BUTTON_STYLE} value={AnnotationToolEnum.LINE_MEASUREMENT} > @@ -1194,7 +1181,7 @@ function ToolSpecificSettings({ isControlOrMetaPressed: boolean; isShiftPressed: boolean; }) { - const showCreateTreeButton = hasSkeleton && adaptedActiveTool === AnnotationToolEnum.SKELETON; + const showSkeletonButtons = hasSkeleton && adaptedActiveTool === AnnotationToolEnum.SKELETON; const showNewBoundingBoxButton = adaptedActiveTool === AnnotationToolEnum.BOUNDING_BOX; const showCreateCellButton = hasVolume && VolumeTools.includes(adaptedActiveTool); const showChangeBrushSizeButton = @@ -1207,7 +1194,6 @@ function ToolSpecificSettings({ ); const isAISelectAvailable = features().segmentAnythingEnabled; const isQuickSelectHeuristic = quickSelectConfig.useHeuristic || !isAISelectAvailable; - const heuristicButtonStyle = isQuickSelectHeuristic ? NARROW_BUTTON_STYLE : ACTIVE_BUTTON_STYLE; const quickSelectTooltipText = isAISelectAvailable ? isQuickSelectHeuristic ? "The quick select tool is now working without AI. Activate AI for better results." @@ -1224,16 +1210,7 @@ function ToolSpecificSettings({ return ( <> - {showCreateTreeButton ? ( - - - - - ) : null} + {showSkeletonButtons ? : null} {showNewBoundingBoxButton ? ( - AI - + @@ -1350,15 +1328,15 @@ function QuickSelectSettingsPopover() { dispatch(showQuickSelectSettingsAction(open)); }} > - - + @@ -1456,30 +1434,26 @@ function ProofReadingComponents() { > - handleToggleAutomaticMeshRendering(!autoRenderMeshes)} > - - + handleToggleSelectiveVisibilityInProofreading(!selectiveVisibilityInProofreading) } > - + ); } diff --git a/frontend/javascripts/oxalis/view/components/button_component.tsx b/frontend/javascripts/oxalis/view/components/button_component.tsx index 0d40c04f964..d620dd001b0 100644 --- a/frontend/javascripts/oxalis/view/components/button_component.tsx +++ b/frontend/javascripts/oxalis/view/components/button_component.tsx @@ -3,7 +3,7 @@ import * as React from "react"; import _ from "lodash"; import FastTooltip, { type FastTooltipPlacement } from "components/fast_tooltip"; -type ButtonComponentProp = ButtonProps & { +type ButtonComponentProps = ButtonProps & { faIcon?: string; tooltipPlacement?: FastTooltipPlacement | undefined; }; @@ -12,8 +12,8 @@ type ButtonComponentProp = ButtonProps & { * after it was clicked. */ -class ButtonComponent extends React.PureComponent { - static defaultProps: ButtonComponentProp = { +class ButtonComponent extends React.PureComponent { + static defaultProps: ButtonComponentProps = { onClick: _.noop, }; @@ -61,4 +61,9 @@ class ButtonComponent extends React.PureComponent { } } +export function ToggleButton(props: { active: boolean } & ButtonComponentProps) { + const { active, ...restProps } = props; + return ; +} + export default ButtonComponent; diff --git a/frontend/javascripts/oxalis/view/layouting/tracing_layout_view.tsx b/frontend/javascripts/oxalis/view/layouting/tracing_layout_view.tsx index 4971fe971ce..031bd1a6478 100644 --- a/frontend/javascripts/oxalis/view/layouting/tracing_layout_view.tsx +++ b/frontend/javascripts/oxalis/view/layouting/tracing_layout_view.tsx @@ -1,4 +1,4 @@ -import { Layout } from "antd"; +import { ConfigProvider, Layout } from "antd"; import ErrorHandling from "libs/error_handling"; import Request from "libs/request"; import Toast from "libs/toast"; @@ -45,6 +45,7 @@ import { determineLayout } from "./default_layout_configs"; import FlexLayoutWrapper from "./flex_layout_wrapper"; import { FloatingMobileControls } from "./floating_mobile_controls"; import app from "app"; +import { NavAndStatusBarTheme } from "theme"; const { Sider } = Layout; @@ -296,32 +297,34 @@ class TracingLayoutView extends React.PureComponent { - {status === "loaded" ? ( -
- { - this.setState({ - activeLayoutName: layoutName, - }); - setActiveLayout(layoutType, layoutName); - }, - saveCurrentLayout: this.saveCurrentLayout, - setAutoSaveLayouts: this.props.setAutoSaveLayouts, - autoSaveLayouts: this.props.autoSaveLayouts, + + {status === "loaded" ? ( +
-
- ) : null} + > + { + this.setState({ + activeLayoutName: layoutName, + }); + setActiveLayout(layoutType, layoutName); + }, + saveCurrentLayout: this.saveCurrentLayout, + setAutoSaveLayouts: this.props.setAutoSaveLayouts, + autoSaveLayouts: this.props.autoSaveLayouts, + }} + /> +
+ ) : null} +
= { + colorPrimary: ColorWKBlue, + colorLink: ColorWKBlue, + colorLinkHover: ColorWKLinkHover, + colorInfo: ColorWKBlue, + blue: ColorWKBlue, + borderRadius: 4, + fontFamily: + '"Nunito", "Monospaced Number", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, sans-serif;', + colorPrimaryBg: ColorWKBlue, +}; + +const darkGlobalToken = theme.getDesignToken({ + token: globalDesignToken, + algorithm: theme.darkAlgorithm, +}); + +const OverridesForNavbarAndStatusBarTheme: ThemeConfig = { + components: { + Radio: { + buttonCheckedBg: darkGlobalToken.colorPrimary, + buttonSolidCheckedBg: darkGlobalToken.colorPrimary, + buttonBg: ColorDarkBg, + }, + Button: { + primaryShadow: "none", + }, + }, + token: { + colorBgContainer: ColorDarkBg, + colorBorder: "#4e4e4e", + colorPrimaryBorder: "#4e4e4e", + // Use a non-transparent color for disabled backgrounds. Otherwise the + // erase-buttons which hide under their neighbors would not hide properly. + colorBgContainerDisabled: "#313131", + }, +}; +export const NavAndStatusBarTheme = _.merge( + getAntdTheme("dark"), + OverridesForNavbarAndStatusBarTheme, +); export function getSystemColorTheme(): Theme { // @ts-ignore @@ -49,18 +94,6 @@ export function getAntdTheme(userTheme: Theme) { }, }; - // Ant Design Customizations - const globalDesignToken: Partial = { - colorPrimary: ColorWKBlue, - colorLink: ColorWKBlue, - colorLinkHover: ColorWKLinkHover, - colorInfo: ColorWKBlue, - blue: ColorWKBlue, - borderRadius: 4, - fontFamily: - '"Nunito", "Monospaced Number", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, sans-serif;', - }; - if (userTheme === "dark") { algorithm = theme.darkAlgorithm; components.Tree = { @@ -69,7 +102,13 @@ export function getAntdTheme(userTheme: Theme) { nodeHoverBg: ColorWKDarkGrey, }; } - return { algorithm, token: globalDesignToken, components }; + return { + algorithm, + // Without the clone(), the default theme shows dark backgrounds in various components. + // Apparently, antd mutates this variable? + token: _.clone(globalDesignToken), + components, + }; } export default function GlobalThemeProvider({ diff --git a/frontend/stylesheets/trace_view/_tracing_view.less b/frontend/stylesheets/trace_view/_tracing_view.less index 32ebf9b48c4..de3e70e69fb 100644 --- a/frontend/stylesheets/trace_view/_tracing_view.less +++ b/frontend/stylesheets/trace_view/_tracing_view.less @@ -353,11 +353,9 @@ img.keyboard-mouse-icon:first-child { .ant-input-number, .ant-radio-button-wrapper, .ant-select > .ant-select-selector{ - background-color: @dark-bg !important; color: @dark-fg !important; - border-color: @dark-border; - &.upgrade-banner-button{ + &.upgrade-banner-button { background-color: var(--ant-button-primary-color) !important; color: var(--ant-color-primary) !important; border-color: var(--ant-button-primary-color); @@ -366,76 +364,19 @@ img.keyboard-mouse-icon:first-child { } &:hover { - border-color: var(--color-blue-zircon) !important; z-index: 1 !important; } - - &:not(:first-child):hover::before { - background-color: var(--color-blue-zircon) !important; - } - - &.ant-btn-disabled, - &.ant-input-disabled, - &.ant-input-number-disabled, - &.ant-radio-button-wrapper-disabled, - &.ant-select-selector-disabled { - color: fade(@dark-fg, 50%); - border-color: @dark-border !important; - - &:hover::before { - background-color: @dark-border !important; - } - } - } .ant-btn[disabled], .ant-btn[disabled]:hover { color: fade(@dark-fg, 50%) !important; - border-color: @dark-border !important; - background-color: @dark-bg !important; } .ant-select-arrow { color: @dark-fg; } - .ant-btn-primary { - background-color: var(--ant-color-primary) !important; - } - - // Radio button - .ant-radio-button-wrapper:first-child { - border-left: 1px solid @dark-border; - &:hover { - border-left: 1px solid var(--color-blue-zircon); - } - } - - .ant-radio-button-wrapper-checked, - .ant-radio-button-wrapper-checked:first-child { - border-color: var(--ant-color-primary); - color: var(--ant-color-primary); - - &::before { - background-color: var(--ant-color-primary) !important; - } - } - - .ant-radio-button-wrapper:not(:first-child)::before { - background-color: @dark-border; - } - - .ant-radio-button-wrapper-checked:not([class*=" ant-radio-button-wrapper-disabled"]).ant-radio-button-wrapper:first-child { - border-right-color: var(--ant-color-primary); - } - - // Primary button in button group - .ant-btn-group .ant-btn-primary:not(:first-child):not(:last-child) { - border-right-color: @dark-border; - border-left-color: @dark-border; - } - .ant-btn-danger { background: var(--ant-color-error-bg); @@ -445,14 +386,6 @@ img.keyboard-mouse-icon:first-child { } } - // Settings icon - .highlight-togglable-button.ant-btn { - background-color: var(--ant-color-primary) !important; - &:not(:hover) { - border-color: var(--ant-color-primary) !important; - } - } - .left-border-button.footer-button, .right-border-button.footer-button { position: absolute; @@ -475,7 +408,7 @@ img.keyboard-mouse-icon:first-child { color: var(--ant-color-text-primary); font-weight: bold; - &:hover{ + &:hover { color: var(--color-blue-zircon); } }