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

Allow resizing Branch and Push/Pull toolbar buttons #18095

Open
wants to merge 16 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions app/src/lib/app-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,12 @@ export interface IAppState {
/** The width of the files list in the pull request files changed view */
readonly pullRequestFilesListWidth: IConstrainedValue

/** The width of the resizable branch drop down button in the toolbar. */
readonly branchDropdownWidth: IConstrainedValue

/** The width of the resizable push/pull button in the toolbar. */
readonly pushPullButtonWidth: IConstrainedValue

/**
* Used to highlight access keys throughout the app when the
* Alt key is pressed. Only applicable on non-macOS platforms.
Expand Down
84 changes: 80 additions & 4 deletions app/src/lib/stores/app-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,12 @@ const stashedFilesWidthConfigKey: string = 'stashed-files-width'
const defaultPullRequestFileListWidth: number = 250
const pullRequestFileListConfigKey: string = 'pull-request-files-width'

const defaultBranchDropdownWidth: number = 230
const branchDropdownWidthConfigKey: string = 'branch-dropdown-width'

const defaultPushPullButtonWidth: number = 230
const pushPullButtonWidthConfigKey: string = 'push-pull-button-width'

const askToMoveToApplicationsFolderDefault: boolean = true
const confirmRepoRemovalDefault: boolean = true
const showCommitLengthWarningDefault: boolean = false
Expand Down Expand Up @@ -475,6 +481,8 @@ export class AppStore extends TypedBaseStore<IAppState> {
private commitSummaryWidth = constrain(defaultCommitSummaryWidth)
private stashedFilesWidth = constrain(defaultStashedFilesWidth)
private pullRequestFileListWidth = constrain(defaultPullRequestFileListWidth)
private branchDropdownWidth = constrain(defaultBranchDropdownWidth)
private pushPullButtonWidth = constrain(defaultPushPullButtonWidth)

private windowState: WindowState | null = null
private windowZoomFactor: number = 1
Expand Down Expand Up @@ -994,6 +1002,8 @@ export class AppStore extends TypedBaseStore<IAppState> {
focusCommitMessage: this.focusCommitMessage,
emoji: this.emoji,
sidebarWidth: this.sidebarWidth,
branchDropdownWidth: this.branchDropdownWidth,
pushPullButtonWidth: this.pushPullButtonWidth,
commitSummaryWidth: this.commitSummaryWidth,
stashedFilesWidth: this.stashedFilesWidth,
pullRequestFilesListWidth: this.pullRequestFileListWidth,
Expand Down Expand Up @@ -2112,6 +2122,12 @@ export class AppStore extends TypedBaseStore<IAppState> {
this.pullRequestFileListWidth = constrain(
getNumber(pullRequestFileListConfigKey, defaultPullRequestFileListWidth)
)
this.branchDropdownWidth = constrain(
getNumber(branchDropdownWidthConfigKey, defaultBranchDropdownWidth)
)
this.pushPullButtonWidth = constrain(
getNumber(pushPullButtonWidthConfigKey, defaultPushPullButtonWidth)
)

this.updateResizableConstraints()
// TODO: Initiliaze here for now... maybe move to dialog mounting
Expand Down Expand Up @@ -2245,11 +2261,12 @@ export class AppStore extends TypedBaseStore<IAppState> {
* dimensions change.
*/
private updateResizableConstraints() {
// The combined width of the branch dropdown and the push pull fetch button
// The combined width of the branch dropdown and the push/pull/fetch button
// Since the repository list toolbar button width is tied to the width of
// the sidebar we can't let it push the branch, and push/pull/fetch buttons
// the sidebar we can't let it push the branch, and push/pull/fetch button
// off screen.
const toolbarButtonsWidth = 460
const toolbarButtonsMinWidth =
defaultPushPullButtonWidth + defaultBranchDropdownWidth

// Start with all the available width
let available = window.innerWidth
Expand All @@ -2260,7 +2277,7 @@ export class AppStore extends TypedBaseStore<IAppState> {
// 220 was determined as the minimum value since it is the smallest width
// that will still fit the placeholder text in the branch selector textbox
// of the history tab
const maxSidebarWidth = available - toolbarButtonsWidth
const maxSidebarWidth = available - toolbarButtonsMinWidth
this.sidebarWidth = constrain(this.sidebarWidth, 220, maxSidebarWidth)

// Now calculate the width we have left to distribute for the other panes
Expand All @@ -2276,6 +2293,23 @@ export class AppStore extends TypedBaseStore<IAppState> {

this.commitSummaryWidth = constrain(this.commitSummaryWidth, 100, filesMax)
this.stashedFilesWidth = constrain(this.stashedFilesWidth, 100, filesMax)

// Update the maximum width available for the branch dropdown resizable.
// The branch dropdown can only be as wide as the available space
// after taking the sidebar and pull/push/fetch button widths.
const branchDropdownMax = available - defaultPushPullButtonWidth
this.branchDropdownWidth = constrain(
this.branchDropdownWidth,
defaultBranchDropdownWidth,
branchDropdownMax
)

const pushPullButtonMaxWidth = available - this.branchDropdownWidth.value
this.pushPullButtonWidth = constrain(
this.pushPullButtonWidth,
defaultPushPullButtonWidth,
pushPullButtonMaxWidth
)
}

/**
Expand Down Expand Up @@ -5165,6 +5199,48 @@ export class AppStore extends TypedBaseStore<IAppState> {
return Promise.resolve()
}

public _setBranchDropdownWidth(width: number): Promise<void> {
this.branchDropdownWidth = { ...this.branchDropdownWidth, value: width }
setNumber(branchDropdownWidthConfigKey, width)
this.updateResizableConstraints()
this.emitUpdate()

return Promise.resolve()
}

public _resetBranchDropdownWidth(): Promise<void> {
this.branchDropdownWidth = {
...this.branchDropdownWidth,
value: defaultBranchDropdownWidth,
}
localStorage.removeItem(branchDropdownWidthConfigKey)
this.updateResizableConstraints()
this.emitUpdate()

return Promise.resolve()
}

public _setPushPullButtonWidth(width: number): Promise<void> {
this.pushPullButtonWidth = { ...this.pushPullButtonWidth, value: width }
setNumber(pushPullButtonWidthConfigKey, width)
this.updateResizableConstraints()
this.emitUpdate()

return Promise.resolve()
}

public _resetPushPullButtonWidth(): Promise<void> {
this.pushPullButtonWidth = {
...this.pushPullButtonWidth,
value: defaultPushPullButtonWidth,
}
localStorage.removeItem(pushPullButtonWidthConfigKey)
this.updateResizableConstraints()
this.emitUpdate()

return Promise.resolve()
}

public _setCommitSummaryWidth(width: number): Promise<void> {
this.commitSummaryWidth = { ...this.commitSummaryWidth, value: width }
setNumber(commitSummaryWidthConfigKey, width)
Expand Down
2 changes: 2 additions & 0 deletions app/src/ui/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3108,6 +3108,7 @@ export class App extends React.Component<IAppProps, IAppState> {
askForConfirmationOnForcePush={this.state.askForConfirmationOnForcePush}
onDropdownStateChanged={this.onPushPullDropdownStateChanged}
enableFocusTrap={enableFocusTrap}
pushPullButtonWidth={this.state.pushPullButtonWidth}
/>
)
}
Expand Down Expand Up @@ -3211,6 +3212,7 @@ export class App extends React.Component<IAppProps, IAppState> {
<BranchDropdown
dispatcher={this.props.dispatcher}
isOpen={isOpen}
branchDropdownWidth={this.state.branchDropdownWidth}
onDropDownStateChanged={this.onBranchDropdownStateChanged}
repository={repository}
repositoryState={selection.state}
Expand Down
35 changes: 35 additions & 0 deletions app/src/ui/dispatcher/dispatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -953,6 +953,41 @@ export class Dispatcher {
return this.appStore._setSidebarWidth(width)
}

/**
* Set the width of the Branch toolbar button to the given value.
* This affects the toolbar button and its dropdown element.
*
* @param width The value for the width of Branch button
*/
public setBranchDropdownWidth(width: number): Promise<void> {
return this.appStore._setBranchDropdownWidth(width)
}

/**
* Reset the width of the Branch toolbar button to its default value.
*/
public resetBranchDropdownWidth(): Promise<void> {
return this.appStore._resetBranchDropdownWidth()
}

/**
* Set the width of the Push/Push toolbar button to the given value.
* This affects the toolbar button and its dropdown element.
*
* @param width The value for the width of Push/Pull button
*/
public setPushPullButtonWidth(width: number): Promise<void> {
return this.appStore._setPushPullButtonWidth(width)
}

/**
* Reset the width of the Push/Pull toolbar button to its default
* value.
*/
public resetPushPullButtonWidth(): Promise<void> {
return this.appStore._resetPushPullButtonWidth()
}

/**
* Set the update banner's visibility
*/
Expand Down
75 changes: 53 additions & 22 deletions app/src/ui/toolbar/branch-dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import { Dispatcher } from '../dispatcher'
import * as octicons from '../octicons/octicons.generated'
import { OcticonSymbol, syncClockwise } from '../octicons'
import { Repository } from '../../models/repository'
import { Resizable } from '../resizable'
import { TipState } from '../../models/tip'
import { ToolbarDropdown, DropdownState } from './dropdown'
import {
FoldoutType,
IConstrainedValue,
IRepositoryState,
isRebaseConflictState,
} from '../../lib/app-state'
Expand All @@ -33,6 +35,9 @@ interface IBranchDropdownProps {
/** The current repository state as derived from AppState */
readonly repositoryState: IRepositoryState

/** The width of the resizable branch dropdown button, as derived from AppState. */
readonly branchDropdownWidth: IConstrainedValue

/** Whether or not the branch dropdown is currently open */
readonly isOpen: boolean

Expand Down Expand Up @@ -188,35 +193,61 @@ export class BranchDropdown extends React.Component<IBranchDropdownProps> {
'nudge-arrow-up': this.props.shouldNudge,
})

// Properties to override the default foldout style for the branch dropdown.
// The min width of the foldout is different from `branchDropdownWidth.min`
// because the branches list foldout min width we want set to 365px instead.
const foldoutStyleOverrides: React.CSSProperties = {
width: this.props.branchDropdownWidth.value,
maxWidth: this.props.branchDropdownWidth.max,
minWidth: 365,
}

return (
<>
<ToolbarDropdown
className="branch-button"
icon={icon}
iconClassName={iconClassName}
title={title}
description={description}
onContextMenu={this.onBranchToolbarButtonContextMenu}
tooltip={isOpen ? undefined : tooltip}
onDropdownStateChanged={this.onDropDownStateChanged}
dropdownContentRenderer={this.renderBranchFoldout}
dropdownState={currentState}
disabled={disabled}
showDisclosureArrow={canOpen}
progressValue={progressValue}
buttonClassName={buttonClassName}
onMouseEnter={this.onMouseEnter}
onlyShowTooltipWhenOverflowed={true}
isOverflowed={isDescriptionOverflowed}
enableFocusTrap={enableFocusTrap}
<Resizable
width={this.props.branchDropdownWidth.value}
onReset={this.onReset}
onResize={this.onResize}
maximumWidth={this.props.branchDropdownWidth.max}
minimumWidth={this.props.branchDropdownWidth.min}
>
{this.renderPullRequestInfo()}
</ToolbarDropdown>
{this.props.showCIStatusPopover && this.renderPopover()}
<ToolbarDropdown
className="branch-button"
icon={icon}
iconClassName={iconClassName}
title={title}
description={description}
foldoutStyleOverrides={foldoutStyleOverrides}
onContextMenu={this.onBranchToolbarButtonContextMenu}
tooltip={isOpen ? undefined : tooltip}
onDropdownStateChanged={this.onDropDownStateChanged}
dropdownContentRenderer={this.renderBranchFoldout}
dropdownState={currentState}
disabled={disabled}
showDisclosureArrow={canOpen}
progressValue={progressValue}
buttonClassName={buttonClassName}
onMouseEnter={this.onMouseEnter}
onlyShowTooltipWhenOverflowed={true}
isOverflowed={isDescriptionOverflowed}
enableFocusTrap={enableFocusTrap}
>
{this.renderPullRequestInfo()}
</ToolbarDropdown>
{this.props.showCIStatusPopover && this.renderPopover()}
</Resizable>
</>
)
}

private onResize = (width: number) => {
this.props.dispatcher.setBranchDropdownWidth(width)
}

private onReset = () => {
this.props.dispatcher.resetBranchDropdownWidth()
}

/**
* Method to capture when the mouse is over the branch dropdown button.
*
Expand Down
24 changes: 21 additions & 3 deletions app/src/ui/toolbar/dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,11 +124,24 @@ export interface IToolbarDropdownProps {
readonly enableFocusTrap?: boolean

/**
* Sets the styles for the dropdown's foldout. Useful for custom positioning
* and sizes.
* Sets the styles for the dropdown's foldout, replacing the defaults.
* Useful for custom positioning and sizes.
*
* Note: If this property is set, the default positioning, size, and
* `foldoutStyleOverrides` property are all ignored.
*/
readonly foldoutStyle?: React.CSSProperties

/**
* Sets additional styles that add to or override the default foldout style.
*
* Use as an alternative to `foldoutStyle`, when only certain properties need
* to be customized and the default style and placement should still apply.
*
* Note: If `foldoutStyle` is set, this property is ignored.
*/
readonly foldoutStyleOverrides?: React.CSSProperties

/**
* Whether the button should displays its disclosure arrow. Defaults to true.
*/
Expand Down Expand Up @@ -352,7 +365,7 @@ export class ToolbarDropdown extends React.Component<
}

return {
position: 'absolute',
position: 'fixed',
Comment on lines -355 to +368
Copy link
Author

Choose a reason for hiding this comment

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

Required to correctly place the dropdown below its toolbar button.

Copy link
Author

@jpedroso jpedroso Jan 29, 2024

Choose a reason for hiding this comment

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

Changing the default properties of the foldout style here might not be the best approach. I’m thinking the branch dropdown should instead provide its own foldoutStyle similar to what the sidebar dropdown has.

top: rect.bottom,
left: 0,
width: '100%',
Expand All @@ -361,6 +374,7 @@ export class ToolbarDropdown extends React.Component<
}

private getFoldoutStyle(): React.CSSProperties | undefined {
// if `foldoutStyle` is set, ignore default style and `foldoutStyleOverrides`
if (this.props.foldoutStyle) {
return this.props.foldoutStyle
}
Expand All @@ -375,11 +389,15 @@ export class ToolbarDropdown extends React.Component<
? { maxHeight: '100%', width: rect.width }
: { height: '100%', minWidth: rect.width }

const overrides: React.CSSProperties =
this.props.foldoutStyleOverrides ?? {}

return {
position: 'absolute',
marginLeft: rect.left,
top: 0,
...heightStyle,
...overrides,
}
}

Expand Down