mirror of
https://github.com/Vencord/Vesktop.git
synced 2025-02-22 21:35:08 +00:00
clean up and optimise Screenshare UI & CSS
This commit is contained in:
parent
67a1847cea
commit
00fb658355
4 changed files with 136 additions and 140 deletions
|
@ -23,11 +23,13 @@ import { Node } from "@vencord/venmic";
|
|||
import type { Dispatch, SetStateAction } from "react";
|
||||
import { addPatch } from "renderer/patches/shared";
|
||||
import { useSettings } from "renderer/settings";
|
||||
import { isLinux, isWindows } from "renderer/utils";
|
||||
import { classNameFactory, isLinux, isWindows } from "renderer/utils";
|
||||
|
||||
const StreamResolutions = ["480", "720", "1080", "1440", "2160"] as const;
|
||||
const StreamFps = ["15", "30", "60"] as const;
|
||||
|
||||
const cl = classNameFactory("vcd-screen-picker-");
|
||||
|
||||
const MediaEngineStore = findStoreLazy("MediaEngineStore");
|
||||
|
||||
export type StreamResolution = (typeof StreamResolutions)[number];
|
||||
|
@ -161,13 +163,21 @@ export function openScreenSharePicker(screens: Source[], skipPicker: boolean) {
|
|||
|
||||
function ScreenPicker({ screens, chooseScreen }: { screens: Source[]; chooseScreen: (id: string) => void }) {
|
||||
return (
|
||||
<div className="vcd-screen-picker-grid">
|
||||
<div className={cl("screen-grid")}>
|
||||
{screens.map(({ id, name, url }) => (
|
||||
<label key={id}>
|
||||
<input type="radio" name="screen" value={id} onChange={() => chooseScreen(id)} />
|
||||
<label key={id} className={cl("screen-label")}>
|
||||
<input
|
||||
type="radio"
|
||||
className={cl("screen-radio")}
|
||||
name="screen"
|
||||
value={id}
|
||||
onChange={() => chooseScreen(id)}
|
||||
/>
|
||||
|
||||
<img src={url} alt="" />
|
||||
<Text variant="text-sm/normal">{name}</Text>
|
||||
<Text className={cl("screen-name")} variant="text-sm/normal">
|
||||
{name}
|
||||
</Text>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
|
@ -187,11 +197,13 @@ function AudioSettingsModal({
|
|||
|
||||
return (
|
||||
<Modals.ModalRoot {...modalProps} size={ModalSize.MEDIUM}>
|
||||
<Modals.ModalHeader className="vcd-screen-picker-header">
|
||||
<Forms.FormTitle tag="h2">Venmic Settings</Forms.FormTitle>
|
||||
<Modals.ModalHeader className={cl("header")}>
|
||||
<Forms.FormTitle tag="h2" className={cl("header-title")}>
|
||||
Venmic Settings
|
||||
</Forms.FormTitle>
|
||||
<Modals.ModalCloseButton onClick={close} />
|
||||
</Modals.ModalHeader>
|
||||
<Modals.ModalContent className="vcd-screen-picker-modal">
|
||||
<Modals.ModalContent className={cl("modal")}>
|
||||
<Switch
|
||||
hideBorder
|
||||
onChange={v => (Settings.audio = { ...Settings.audio, workaround: v })}
|
||||
|
@ -295,7 +307,7 @@ function AudioSettingsModal({
|
|||
Device Selection
|
||||
</Switch>
|
||||
</Modals.ModalContent>
|
||||
<Modals.ModalFooter className="vcd-screen-picker-footer">
|
||||
<Modals.ModalFooter className={cl("footer")}>
|
||||
<Button color={Button.Colors.TRANSPARENT} onClick={close}>
|
||||
Back
|
||||
</Button>
|
||||
|
@ -304,6 +316,34 @@ function AudioSettingsModal({
|
|||
);
|
||||
}
|
||||
|
||||
function OptionRadio(props: {
|
||||
options: Array<string> | ReadonlyArray<string>;
|
||||
labels?: Array<string>;
|
||||
settingsKey: string;
|
||||
settings: StreamSettings;
|
||||
setSettings: Dispatch<SetStateAction<StreamSettings>>;
|
||||
}) {
|
||||
const { options, setSettings, settings, settingsKey, labels } = props;
|
||||
|
||||
return (
|
||||
<div className={cl("option-radios")}>
|
||||
{(options as string[]).map((option, idx) => (
|
||||
<label className={cl("option-radio")} data-checked={settings[settingsKey] === option} key={option}>
|
||||
<Text variant="text-sm/bold">{labels?.[idx] ?? option}</Text>
|
||||
<input
|
||||
className={cl("option-input")}
|
||||
type="radio"
|
||||
name="fps"
|
||||
value={option}
|
||||
checked={settings[settingsKey] === option}
|
||||
onChange={() => setSettings(s => ({ ...s, [settingsKey]: option }))}
|
||||
/>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function StreamSettings({
|
||||
source,
|
||||
settings,
|
||||
|
@ -340,88 +380,47 @@ function StreamSettings({
|
|||
return (
|
||||
<div>
|
||||
<Forms.FormTitle>What you're streaming</Forms.FormTitle>
|
||||
<Card className="vcd-screen-picker-card vcd-screen-picker-preview">
|
||||
<img
|
||||
src={thumb}
|
||||
alt=""
|
||||
className={isLinux ? "vcd-screen-picker-preview-img-linux" : "vcd-screen-picker-preview-img"}
|
||||
/>
|
||||
<Card className={cl("card", "preview")}>
|
||||
<img src={thumb} alt="" className={cl(isLinux ? "preview-img-linux" : "preview-img")} />
|
||||
<Text variant="text-sm/normal">{source.name}</Text>
|
||||
</Card>
|
||||
|
||||
<Forms.FormTitle>Stream Settings</Forms.FormTitle>
|
||||
|
||||
<Card className="vcd-screen-picker-card">
|
||||
<div className="vcd-screen-picker-quality">
|
||||
<section>
|
||||
<Card className={cl("card")}>
|
||||
<div className={cl("quality")}>
|
||||
<section className={cl("quality-section")}>
|
||||
<Forms.FormTitle>Resolution</Forms.FormTitle>
|
||||
<div className="vcd-screen-picker-radios">
|
||||
{StreamResolutions.map(res => (
|
||||
<label className="vcd-screen-picker-radio" data-checked={settings.resolution === res}>
|
||||
<Text variant="text-sm/bold">{res}</Text>
|
||||
<input
|
||||
type="radio"
|
||||
name="resolution"
|
||||
value={res}
|
||||
checked={settings.resolution === res}
|
||||
onChange={() => setSettings(s => ({ ...s, resolution: res }))}
|
||||
<OptionRadio
|
||||
options={StreamResolutions}
|
||||
settingsKey="resolution"
|
||||
settings={settings}
|
||||
setSettings={setSettings}
|
||||
/>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<section className={cl("quality-section")}>
|
||||
<Forms.FormTitle>Frame Rate</Forms.FormTitle>
|
||||
<div className="vcd-screen-picker-radios">
|
||||
{StreamFps.map(fps => (
|
||||
<label className="vcd-screen-picker-radio" data-checked={settings.fps === fps}>
|
||||
<Text variant="text-sm/bold">{fps}</Text>
|
||||
<input
|
||||
type="radio"
|
||||
name="fps"
|
||||
value={fps}
|
||||
checked={settings.fps === fps}
|
||||
onChange={() => setSettings(s => ({ ...s, fps }))}
|
||||
<OptionRadio
|
||||
options={StreamFps}
|
||||
settingsKey="fps"
|
||||
settings={settings}
|
||||
setSettings={setSettings}
|
||||
/>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<div className="vcd-screen-picker-quality">
|
||||
<section>
|
||||
<div className={cl("quality")}>
|
||||
<section className={cl("quality-section")}>
|
||||
<Forms.FormTitle>Content Type</Forms.FormTitle>
|
||||
<div>
|
||||
<div className="vcd-screen-picker-radios">
|
||||
<label
|
||||
className="vcd-screen-picker-radio"
|
||||
data-checked={settings.contentHint === "motion"}
|
||||
>
|
||||
<Text variant="text-sm/bold">Prefer Smoothness</Text>
|
||||
<input
|
||||
type="radio"
|
||||
name="contenthint"
|
||||
value="motion"
|
||||
checked={settings.contentHint === "motion"}
|
||||
onChange={() => setSettings(s => ({ ...s, contentHint: "motion" }))}
|
||||
<OptionRadio
|
||||
options={["motion", "detail"]}
|
||||
labels={["Prefer Smoothness", "Prefer Clarity"]}
|
||||
settingsKey="contentHint"
|
||||
settings={settings}
|
||||
setSettings={setSettings}
|
||||
/>
|
||||
</label>
|
||||
<label
|
||||
className="vcd-screen-picker-radio"
|
||||
data-checked={settings.contentHint === "detail"}
|
||||
>
|
||||
<Text variant="text-sm/bold">Prefer Clarity</Text>
|
||||
<input
|
||||
type="radio"
|
||||
name="contenthint"
|
||||
value="detail"
|
||||
checked={settings.contentHint === "detail"}
|
||||
onChange={() => setSettings(s => ({ ...s, contentHint: "detail" }))}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div className="vcd-screen-picker-hint-description">
|
||||
<div className={cl("hint-description")}>
|
||||
<p>
|
||||
Choosing "Prefer Clarity" will result in a significantly lower framerate in exchange
|
||||
for a much sharper and clearer image.
|
||||
|
@ -433,7 +432,7 @@ function StreamSettings({
|
|||
value={settings.audio}
|
||||
onChange={checked => setSettings(s => ({ ...s, audio: checked }))}
|
||||
hideBorder
|
||||
className="vcd-screen-picker-audio"
|
||||
className={cl("audio")}
|
||||
>
|
||||
Stream With Audio
|
||||
</Switch>
|
||||
|
@ -639,7 +638,7 @@ function AudioSourcePickerLinux({
|
|||
|
||||
return (
|
||||
<>
|
||||
<div className={includeSources === "Entire System" ? "vcd-screen-picker-quality" : undefined}>
|
||||
<div className={cl({ quality: includeSources === "Entire System" })}>
|
||||
<section>
|
||||
<Forms.FormTitle>{loading ? "Loading Sources..." : "Audio Sources"}</Forms.FormTitle>
|
||||
<Select
|
||||
|
@ -675,11 +674,7 @@ function AudioSourcePickerLinux({
|
|||
</section>
|
||||
)}
|
||||
</div>
|
||||
<Button
|
||||
color={Button.Colors.TRANSPARENT}
|
||||
onClick={openSettings}
|
||||
className="vcd-screen-picker-settings-button"
|
||||
>
|
||||
<Button color={Button.Colors.TRANSPARENT} onClick={openSettings} className={cl("settings-button")}>
|
||||
Open Audio Settings
|
||||
</Button>
|
||||
</>
|
||||
|
@ -710,11 +705,11 @@ function ModalComponent({
|
|||
|
||||
return (
|
||||
<Modals.ModalRoot {...modalProps} size={ModalSize.MEDIUM}>
|
||||
<Modals.ModalHeader className="vcd-screen-picker-header">
|
||||
<Modals.ModalHeader className={cl("header")}>
|
||||
<Forms.FormTitle tag="h2">ScreenShare</Forms.FormTitle>
|
||||
<Modals.ModalCloseButton onClick={close} />
|
||||
</Modals.ModalHeader>
|
||||
<Modals.ModalContent className="vcd-screen-picker-modal">
|
||||
<Modals.ModalContent className={cl("modal")}>
|
||||
{!selected ? (
|
||||
<ScreenPicker screens={screens} chooseScreen={setSelected} />
|
||||
) : (
|
||||
|
@ -726,7 +721,7 @@ function ModalComponent({
|
|||
/>
|
||||
)}
|
||||
</Modals.ModalContent>
|
||||
<Modals.ModalFooter className="vcd-screen-picker-footer">
|
||||
<Modals.ModalFooter className={cl("footer")}>
|
||||
<Button
|
||||
disabled={!selected}
|
||||
onClick={() => {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
padding: 1em;
|
||||
}
|
||||
|
||||
.vcd-screen-picker-header h1 {
|
||||
.vcd-screen-picker-header-title {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
@ -15,23 +15,20 @@
|
|||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.vcd-screen-picker-grid {
|
||||
|
||||
/* Screen Grid */
|
||||
.vcd-screen-picker-screen-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 2em 1em;
|
||||
}
|
||||
|
||||
.vcd-screen-picker-grid input {
|
||||
.vcd-screen-picker-screen-radio {
|
||||
appearance: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.vcd-screen-picker-selected img {
|
||||
border: 2px solid var(--brand-500);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.vcd-screen-picker-grid label {
|
||||
.vcd-screen-picker-screen-label {
|
||||
overflow: hidden;
|
||||
padding: 8px;
|
||||
cursor: pointer;
|
||||
|
@ -39,11 +36,11 @@
|
|||
justify-items: center;
|
||||
}
|
||||
|
||||
.vcd-screen-picker-grid label:hover {
|
||||
.vcd-screen-picker-screen-label:hover {
|
||||
outline: 2px solid var(--brand-500);
|
||||
}
|
||||
|
||||
.vcd-screen-picker-grid div {
|
||||
.vcd-screen-picker-screen-name {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
|
@ -75,37 +72,48 @@
|
|||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.vcd-screen-picker-radio input {
|
||||
display: none;
|
||||
|
||||
/* Option Radios */
|
||||
|
||||
.vcd-screen-picker-option-radios {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.vcd-screen-picker-radio {
|
||||
.vcd-screen-picker-option-radio {
|
||||
flex: 1 1 auto;
|
||||
text-align: center;
|
||||
background-color: var(--background-secondary);
|
||||
border: 1px solid var(--primary-800);
|
||||
padding: 0.3em;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.vcd-screen-picker-radio h2 {
|
||||
margin: 0;
|
||||
.vcd-screen-picker-option-radio:first-child {
|
||||
border-radius: 3px 0 0 3px;
|
||||
}
|
||||
|
||||
.vcd-screen-picker-radio[data-checked="true"] {
|
||||
.vcd-screen-picker-option-radio:last-child {
|
||||
border-radius: 0 3px 3px 0;
|
||||
}
|
||||
|
||||
.vcd-screen-picker-option-input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.vcd-screen-picker-option-radio[data-checked="true"] {
|
||||
background-color: var(--brand-500);
|
||||
border-color: var(--brand-500);
|
||||
}
|
||||
|
||||
.vcd-screen-picker-radio[data-checked="true"] h2 {
|
||||
color: var(--interactive-active);
|
||||
}
|
||||
|
||||
.vcd-screen-picker-quality {
|
||||
display: flex;
|
||||
gap: 1em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.vcd-screen-picker-quality section {
|
||||
.vcd-screen-picker-quality-section {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
|
@ -114,24 +122,6 @@
|
|||
margin-top: 0.3rem;
|
||||
}
|
||||
|
||||
.vcd-screen-picker-radios {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.vcd-screen-picker-radios label {
|
||||
flex: 1 1 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.vcd-screen-picker-radios label:first-child {
|
||||
border-radius: 3px 0 0 3px;
|
||||
}
|
||||
|
||||
.vcd-screen-picker-radios label:last-child {
|
||||
border-radius: 0 3px 3px 0;
|
||||
}
|
||||
|
||||
.vcd-screen-picker-audio {
|
||||
margin-bottom: 0;
|
||||
|
|
|
@ -1,15 +1,3 @@
|
|||
/* Download Desktop button in guilds list */
|
||||
[class^=listItem_]:has([data-list-item-id=guildsnav___app-download-button]),
|
||||
[class^=listItem_]:has(+ [class^=listItem_] [data-list-item-id=guildsnav___app-download-button]) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* FIXME: remove this once Discord fixes their css to not explode scrollbars on chromium >=121 */
|
||||
* {
|
||||
scrollbar-width: unset !important;
|
||||
scrollbar-color: unset !important;
|
||||
}
|
||||
|
||||
/* Workaround for making things in the draggable area clickable again on macOS */
|
||||
.platform-osx [class*=topic_], .platform-osx [class*=notice_] button {
|
||||
-webkit-app-region: no-drag;
|
||||
|
|
|
@ -18,3 +18,26 @@ const { platform } = navigator;
|
|||
export const isWindows = platform.startsWith("Win");
|
||||
export const isMac = platform.startsWith("Mac");
|
||||
export const isLinux = platform.startsWith("Linux");
|
||||
|
||||
type ClassNameFactoryArg = string | string[] | Record<string, unknown> | false | null | undefined | 0 | "";
|
||||
/**
|
||||
* @param prefix The prefix to add to each class, defaults to `""`
|
||||
* @returns A classname generator function
|
||||
* @example
|
||||
* const cl = classNameFactory("plugin-");
|
||||
*
|
||||
* cl("base", ["item", "editable"], { selected: null, disabled: true })
|
||||
* // => "plugin-base plugin-item plugin-editable plugin-disabled"
|
||||
*/
|
||||
export const classNameFactory =
|
||||
(prefix: string = "") =>
|
||||
(...args: ClassNameFactoryArg[]) => {
|
||||
const classNames = new Set<string>();
|
||||
for (const arg of args) {
|
||||
if (arg && typeof arg === "string") classNames.add(arg);
|
||||
else if (Array.isArray(arg)) arg.forEach(name => classNames.add(name));
|
||||
else if (arg && typeof arg === "object")
|
||||
Object.entries(arg).forEach(([name, value]) => value && classNames.add(name));
|
||||
}
|
||||
return Array.from(classNames, name => prefix + name).join(" ");
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue