mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-19 10:58:04 +08:00
Rename project structure with /core and /client
This commit is contained in:
19
client/web/src/components/layout/LayoutCol.vue
Normal file
19
client/web/src/components/layout/LayoutCol.vue
Normal file
@@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<div :class="['layout-col']">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.layout-col {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({});
|
||||
</script>
|
||||
19
client/web/src/components/layout/LayoutRow.vue
Normal file
19
client/web/src/components/layout/LayoutRow.vue
Normal file
@@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<div :class="['layout-row']">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.layout-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-grow: 1;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({});
|
||||
</script>
|
||||
195
client/web/src/components/panel-system/DockablePanel.vue
Normal file
195
client/web/src/components/panel-system/DockablePanel.vue
Normal file
@@ -0,0 +1,195 @@
|
||||
<template>
|
||||
<div class="panel">
|
||||
<div class="tab-bar" :class="{ 'min-widths': tabMinWidths }">
|
||||
<div class="tab-group">
|
||||
<div class="tab" :class="{ active: tabIndex === tabActiveIndex }" v-for="(tabLabel, tabIndex) in tabLabels" :key="tabLabel">
|
||||
<span>{{tabLabel}}</span>
|
||||
<button v-if="tabCloseButtons">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16">
|
||||
<polygon points="12,5 11,4 8,7 5,4 4,5 7,8 4,11 5,12 8,9 11,12 12,11 9,8" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-options-ellipsis">
|
||||
<svg viewBox="0 0 16 24">
|
||||
<circle cx="8" cy="8" r="1.5" />
|
||||
<circle cx="8" cy="12" r="1.5" />
|
||||
<circle cx="8" cy="16" r="1.5" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-content">
|
||||
<component :is="panelType" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.panel {
|
||||
background: #111;
|
||||
border-radius: 8px;
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
|
||||
.tab-bar {
|
||||
height: 28px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
&.min-widths .tab-group .tab {
|
||||
min-width: 120px;
|
||||
max-width: 360px;
|
||||
}
|
||||
|
||||
.tab-group {
|
||||
flex: 1 1 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
overflow: hidden;
|
||||
|
||||
.tab {
|
||||
height: 100%;
|
||||
padding: 0 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
|
||||
&.active {
|
||||
background: #333;
|
||||
border-radius: 8px 8px 0 0;
|
||||
position: relative;
|
||||
|
||||
&::before, &::after {
|
||||
content: "";
|
||||
width: 16px;
|
||||
height: 8px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
box-shadow: #333;
|
||||
}
|
||||
|
||||
&::before {
|
||||
left: -16px;
|
||||
border-bottom-right-radius: 8px;
|
||||
box-shadow: 8px 0 0 0 #333;
|
||||
}
|
||||
|
||||
&::after {
|
||||
right: -16px;
|
||||
border-bottom-left-radius: 8px;
|
||||
box-shadow: -8px 0 0 0 #333;
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
flex: 1 1 100%;
|
||||
overflow-x: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
// Height and line-height required because https://stackoverflow.com/a/21611191/775283
|
||||
height: 100%;
|
||||
line-height: 28px;
|
||||
}
|
||||
|
||||
button {
|
||||
flex: 0 0 auto;
|
||||
outline: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: none;
|
||||
color: #ddd;
|
||||
font-weight: bold;
|
||||
font-size: 10px;
|
||||
border-radius: 2px;
|
||||
margin-left: 8px;
|
||||
fill: #ddd;
|
||||
|
||||
&:hover {
|
||||
background: #555;
|
||||
color: white;
|
||||
fill: white;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.active) + .tab:not(.active) {
|
||||
margin-left: 1px;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: -1px;
|
||||
width: 1px;
|
||||
height: 16px;
|
||||
background: #444;
|
||||
}
|
||||
}
|
||||
|
||||
&:last-of-type:not(.active) {
|
||||
margin-right: 1px;
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
right: -1px;
|
||||
width: 1px;
|
||||
height: 16px;
|
||||
background: #444;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.panel-options-ellipsis {
|
||||
width: 16px;
|
||||
height: 24px;
|
||||
margin: 2px 4px;
|
||||
|
||||
svg {
|
||||
width: 16px;
|
||||
height: 24px;
|
||||
border-radius: 2px;
|
||||
fill: #ddd;
|
||||
}
|
||||
|
||||
&:hover svg {
|
||||
background: #555;
|
||||
fill: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.panel-content {
|
||||
background: #333;
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
import Viewport from "../panels/ViewportPanel.vue";
|
||||
import Properties from "../panels/PropertiesPanel.vue";
|
||||
import Layers from "../panels/LayersPanel.vue";
|
||||
import Minimap from "../panels/MinimapPanel.vue";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
Viewport,
|
||||
Properties,
|
||||
Layers,
|
||||
Minimap,
|
||||
},
|
||||
props: {
|
||||
tabMinWidths: { type: Boolean, default: false },
|
||||
tabCloseButtons: { type: Boolean, default: false },
|
||||
tabLabels: { type: Array, required: true },
|
||||
tabActiveIndex: { type: Number, required: true },
|
||||
panelType: { type: String, required: true },
|
||||
},
|
||||
});
|
||||
</script>
|
||||
54
client/web/src/components/panel-system/PanelArea.vue
Normal file
54
client/web/src/components/panel-system/PanelArea.vue
Normal file
@@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<LayoutRow class="dockable-grid-subdivision">
|
||||
<LayoutCol class="dockable-grid-subdivision" style="flex-grow: 1597;">
|
||||
<DockablePanel :panelType="'Viewport'" :tabCloseButtons="true" :tabMinWidths="true" :tabLabels="['X-35B Over Death Valley*', 'Document 2', 'Document 3', 'Document 4', 'Document 5']" :tabActiveIndex="0" />
|
||||
</LayoutCol>
|
||||
<LayoutCol class="dockable-grid-resize-gutter"></LayoutCol>
|
||||
<LayoutCol class="dockable-grid-subdivision" style="flex-grow: 319;">
|
||||
<LayoutRow class="dockable-grid-subdivision">
|
||||
<DockablePanel :panelType="'Properties'" :tabLabels="['Properties', 'Typography', 'Colors']" :tabActiveIndex="0" />
|
||||
</LayoutRow>
|
||||
<LayoutRow class="dockable-grid-resize-gutter"></LayoutRow>
|
||||
<LayoutRow class="dockable-grid-subdivision">
|
||||
<DockablePanel :panelType="'Layers'" :tabLabels="['Layers']" :tabActiveIndex="0" />
|
||||
</LayoutRow>
|
||||
<LayoutRow class="dockable-grid-resize-gutter"></LayoutRow>
|
||||
<LayoutRow class="dockable-grid-subdivision" style="flex-grow: 0; height: 0;">
|
||||
<DockablePanel :panelType="'Minimap'" :tabLabels="['Minimap', 'Brushes', 'Links']" :tabActiveIndex="0" />
|
||||
</LayoutRow>
|
||||
</LayoutCol>
|
||||
</LayoutRow>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.dockable-grid-subdivision {
|
||||
min-height: 28px;
|
||||
}
|
||||
|
||||
.dockable-grid-resize-gutter {
|
||||
flex: 0 0 4px;
|
||||
|
||||
&.layout-row {
|
||||
cursor: ns-resize;
|
||||
}
|
||||
|
||||
&.layout-col {
|
||||
cursor: ew-resize;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
import LayoutRow from "../layout/LayoutRow.vue";
|
||||
import LayoutCol from "../layout/LayoutCol.vue";
|
||||
import DockablePanel from "./DockablePanel.vue";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
LayoutRow,
|
||||
LayoutCol,
|
||||
DockablePanel,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
21
client/web/src/components/panels/LayersPanel.vue
Normal file
21
client/web/src/components/panels/LayersPanel.vue
Normal file
@@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
</style>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
// import LayoutRow from "../layout/LayoutRow.vue";
|
||||
// import LayoutCol from "../layout/LayoutCol.vue";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
},
|
||||
props: {
|
||||
},
|
||||
});
|
||||
</script>
|
||||
21
client/web/src/components/panels/MinimapPanel.vue
Normal file
21
client/web/src/components/panels/MinimapPanel.vue
Normal file
@@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
</style>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
// import LayoutRow from "../layout/LayoutRow.vue";
|
||||
// import LayoutCol from "../layout/LayoutCol.vue";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
},
|
||||
props: {
|
||||
},
|
||||
});
|
||||
</script>
|
||||
21
client/web/src/components/panels/PropertiesPanel.vue
Normal file
21
client/web/src/components/panels/PropertiesPanel.vue
Normal file
@@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
</style>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
// import LayoutRow from "../layout/LayoutRow.vue";
|
||||
// import LayoutCol from "../layout/LayoutCol.vue";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
},
|
||||
props: {
|
||||
},
|
||||
});
|
||||
</script>
|
||||
81
client/web/src/components/panels/ViewportPanel.vue
Normal file
81
client/web/src/components/panels/ViewportPanel.vue
Normal file
@@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<LayoutCol :class="'viewport'">
|
||||
<LayoutRow :class="'options-bar'">
|
||||
<div class="left side">
|
||||
<!-- <span class="label">Select</span>
|
||||
<div class="divider"></div> -->
|
||||
</div>
|
||||
<div class="spacer"></div>
|
||||
<div class="right side">
|
||||
<!-- <span class="label">Layer 1</span> -->
|
||||
</div>
|
||||
</LayoutRow>
|
||||
<LayoutRow :class="'tools-and-viewport'">
|
||||
<LayoutCol :class="'tools'"></LayoutCol>
|
||||
<LayoutCol :class="'viewport-container'"></LayoutCol>
|
||||
</LayoutRow>
|
||||
</LayoutCol>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.viewport {
|
||||
height: 100%;
|
||||
|
||||
.options-bar {
|
||||
flex: 0 0 32px;
|
||||
|
||||
.side {
|
||||
height: 100%;
|
||||
flex: 0 1 auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0 4px;
|
||||
|
||||
> * {
|
||||
margin: 0 4px;
|
||||
}
|
||||
|
||||
.label {
|
||||
white-space: nowrap;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.divider {
|
||||
width: 1px;
|
||||
height: 24px;
|
||||
background: #888;
|
||||
}
|
||||
}
|
||||
|
||||
.spacer {
|
||||
flex: 1 1 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.tools-and-viewport {
|
||||
.tools {
|
||||
flex: 0 0 32px;
|
||||
}
|
||||
|
||||
.viewport-container {
|
||||
background: #111;
|
||||
flex: 1 1 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
import LayoutRow from "../layout/LayoutRow.vue";
|
||||
import LayoutCol from "../layout/LayoutCol.vue";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
LayoutRow,
|
||||
LayoutCol,
|
||||
},
|
||||
props: {
|
||||
},
|
||||
});
|
||||
</script>
|
||||
66
client/web/src/components/window/MainWindow.vue
Normal file
66
client/web/src/components/window/MainWindow.vue
Normal file
@@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<LayoutCol class="main-window">
|
||||
<LayoutRow :class="'title-bar'">
|
||||
<TitleBar :platform="platform" :maximized="maximized" />
|
||||
</LayoutRow>
|
||||
<LayoutRow :class="'panel-container'">
|
||||
<PanelArea />
|
||||
</LayoutRow>
|
||||
<LayoutRow :class="'footer-bar'">
|
||||
<FooterBar />
|
||||
</LayoutRow>
|
||||
</LayoutCol>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.main-window {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.title-bar {
|
||||
height: 28px;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.panel-container {
|
||||
flex: 1 1 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.footer-bar {
|
||||
height: 24px;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
import LayoutRow from "../layout/LayoutRow.vue";
|
||||
import LayoutCol from "../layout/LayoutCol.vue";
|
||||
import PanelArea from "../panel-system/PanelArea.vue";
|
||||
import TitleBar from "./title-bar/TitleBar.vue";
|
||||
import FooterBar from "./footer-bar/FooterBar.vue";
|
||||
|
||||
export enum ApplicationPlatform {
|
||||
"Windows" = "Windows",
|
||||
"Mac" = "Mac",
|
||||
"Linux" = "Linux",
|
||||
"Web" = "Web",
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
LayoutRow,
|
||||
LayoutCol,
|
||||
TitleBar,
|
||||
PanelArea,
|
||||
FooterBar,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
platform: ApplicationPlatform.Windows,
|
||||
maximized: true,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
73
client/web/src/components/window/footer-bar/FooterBar.vue
Normal file
73
client/web/src/components/window/footer-bar/FooterBar.vue
Normal file
@@ -0,0 +1,73 @@
|
||||
<template>
|
||||
<div class="hint-area footer-area">
|
||||
<InputHint :inputMouse="'LMB'">Select Object</InputHint>
|
||||
<span class="plus">+</span>
|
||||
<InputHint :inputKeys="['⇧']">Grow/Shrink Selection</InputHint>
|
||||
<div class="divider"></div>
|
||||
<InputHint :inputMouse="'LMBDrag'">Select Area</InputHint>
|
||||
<span class="plus">+</span>
|
||||
<InputHint :inputKeys="['⇧']">Grow/Shrink Selection</InputHint>
|
||||
<div class="divider"></div>
|
||||
<InputHint :inputKeys="['G']">Grab Selected</InputHint>
|
||||
<InputHint :inputKeys="['R']">Rotate Selected</InputHint>
|
||||
<InputHint :inputKeys="['S']">Scale Selected</InputHint>
|
||||
<div class="divider"></div>
|
||||
<InputHint :inputKeys="['↑', '→', '↓', '←']">Nudge Selected</InputHint>
|
||||
<span class="plus">+</span>
|
||||
<InputHint :inputKeys="['⇧']">Big Increment Nudge</InputHint>
|
||||
<div class="divider"></div>
|
||||
<InputHint :inputKeys="['Alt']" :inputMouse="'LMBDrag'">Move Duplicate</InputHint>
|
||||
<InputHint :inputKeys="['Ctrl', 'D']">Duplicate</InputHint>
|
||||
</div>
|
||||
<div class="status-area footer-area">
|
||||
<div>
|
||||
<span>Graphite 0.0.1</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.footer-area {
|
||||
display: flex;
|
||||
|
||||
&.hint-area {
|
||||
flex: 1 1 100%;
|
||||
}
|
||||
|
||||
&.status-area div {
|
||||
margin: 0 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.plus {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.divider {
|
||||
width: 1px;
|
||||
height: 16px;
|
||||
margin: 4px 0;
|
||||
background: #888;
|
||||
}
|
||||
|
||||
.input-hint + .input-hint {
|
||||
margin-left: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
import InputHint from "./InputHint.vue";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
InputHint,
|
||||
},
|
||||
});
|
||||
|
||||
</script>
|
||||
168
client/web/src/components/window/footer-bar/InputHint.vue
Normal file
168
client/web/src/components/window/footer-bar/InputHint.vue
Normal file
@@ -0,0 +1,168 @@
|
||||
<template>
|
||||
<div class="input-hint">
|
||||
<span class="input-key" v-for="inputKey in inputKeys" :key="inputKey" :class="keyCapWidth(inputKey)">
|
||||
{{inputKey}}
|
||||
</span>
|
||||
<span class="input-mouse" v-if="inputMouse">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" v-html="getMouseIconInnerSVG"></svg>
|
||||
</span>
|
||||
<span class="hint-text">
|
||||
<slot></slot>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.input-hint {
|
||||
height: 100%;
|
||||
margin: 0 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
|
||||
.input-key, .input-mouse {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.input-key {
|
||||
font-family: "Inconsolata", monospace;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
color: #111;
|
||||
background: #ddd;
|
||||
border: 1px;
|
||||
box-sizing: border-box;
|
||||
border-style: solid;
|
||||
border-color: #888;
|
||||
border-radius: 4px;
|
||||
height: 16px;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
.input-key.width-16 {
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
.input-key.width-24 {
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
.input-key.width-32 {
|
||||
width: 32px;
|
||||
}
|
||||
|
||||
.input-key.width-40 {
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
.input-key.width-48 {
|
||||
width: 48px;
|
||||
}
|
||||
|
||||
.input-mouse {
|
||||
font-size: 0;
|
||||
|
||||
.bright {
|
||||
fill: #ddd;
|
||||
}
|
||||
|
||||
.dim {
|
||||
fill: #888;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export enum MouseInputInteraction {
|
||||
"None" = "None",
|
||||
"LMB" = "LMB",
|
||||
"RMB" = "RMB",
|
||||
"MMB" = "MMB",
|
||||
"ScrollUp" = "ScrollUp",
|
||||
"ScrollDown" = "ScrollDown",
|
||||
"Drag" = "Drag",
|
||||
"LMBDrag" = "LMBDrag",
|
||||
"RMBDrag" = "RMBDrag",
|
||||
"MMBDrag" = "MMBDrag",
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
inputKeys: { type: Array, default: () => [] },
|
||||
inputMouse: { type: String },
|
||||
},
|
||||
methods: {
|
||||
keyCapWidth(keyText: string) {
|
||||
return `width-${keyText.length * 8 + 8}`;
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
getMouseIconInnerSVG(): string {
|
||||
switch (this.inputMouse) {
|
||||
case MouseInputInteraction.None: return `
|
||||
<path class="dim" d="M9,7c0,0.55-0.45,1-1,1l0,0C7.45,8,7,7.55,7,7V4.5c0-0.55,0.45-1,1-1l0,0c0.55,0,1,0.45,1,1V7z" />
|
||||
<path class="dim" d="M10,2c1.1,0,2,0.9,2,2v6c0,2.21-1.79,4-4,4s-4-1.79-4-4V4c0-1.1,0.9-2,2-2H10 M10,1H6
|
||||
C4.35,1,3,2.35,3,4v6c0,2.76,2.24,5,5,5s5-2.24,5-5V4C13,2.35,11.65,1,10,1L10,1z" />`;
|
||||
case MouseInputInteraction.LMB: return `
|
||||
<path class="bright" d="M8,1H6C4.35,1,3,2.35,3,4v4h5V1z" />
|
||||
<path class="dim" d="M10,1H9v1h1c1.1,0,2,0.9,2,2v6c0,2.21-1.79,4-4,4s-4-1.79-4-4V9H3v1c0,2.76,2.24,5,5,5s5-2.24,5-5V4
|
||||
C13,2.35,11.65,1,10,1z" />`;
|
||||
case MouseInputInteraction.RMB: return `
|
||||
<path class="dim" d="M8,1h2c1.65,0,3,1.35,3,3v4H8V1z" />
|
||||
<path class="bright" d="M6,1h1v1H6C4.9,2,4,2.9,4,4v6c0,2.21,1.79,4,4,4s4-1.79,4-4V9h1v1c0,2.76-2.24,5-5,5s-5-2.24-5-5V4
|
||||
C3,2.35,4.35,1,6,1z" />`;
|
||||
case MouseInputInteraction.MMB: return `
|
||||
<path class="bright" d="M9,7c0,0.55-0.45,1-1,1l0,0C7.45,8,7,7.55,7,7V4.5c0-0.55,0.45-1,1-1l0,0c0.55,0,1,0.45,1,1V7z" />
|
||||
<path class="dim" d="M10,2c1.1,0,2,0.9,2,2v6c0,2.21-1.79,4-4,4s-4-1.79-4-4V4c0-1.1,0.9-2,2-2H10 M10,1H6
|
||||
C4.35,1,3,2.35,3,4v6c0,2.76,2.24,5,5,5s5-2.24,5-5V4C13,2.35,11.65,1,10,1L10,1z" />`;
|
||||
case MouseInputInteraction.ScrollUp: return `
|
||||
<polygon class="bright" points="10.5,4 8,2 5.5,4 5.5,2 8,0 10.5,2" />
|
||||
<polygon class="bright" points="10.5,8 8,6 5.5,8 5.5,6 8,4 10.5,6" />
|
||||
<path class="dim" d="M11.5,1.42v1.28C11.8,3.06,12,3.5,12,4v6c0,2.21-1.79,4-4,4s-4-1.79-4-4V4c0-0.5,0.2-0.94,0.5-1.29
|
||||
V1.42C3.61,1.94,3,2.9,3,4v6c0,2.76,2.24,5,5,5s5-2.24,5-5V4C13,2.9,12.39,1.94,11.5,1.42z" />`;
|
||||
case MouseInputInteraction.ScrollDown: return `
|
||||
<polygon class="bright" points="5.5,4 8,6 10.5,4 10.5,6 8,8 5.5,6" />
|
||||
<polygon class="bright" points="5.5,0 8,2 10.5,0 10.5,2 8,4 5.5,2" />
|
||||
<path class="dim" d="M11.5,1.42v1.28C11.8,3.06,12,3.5,12,4v6c0,2.21-1.79,4-4,4s-4-1.79-4-4V4c0-0.5,0.2-0.94,0.5-1.29
|
||||
V1.42C3.61,1.94,3,2.9,3,4v6c0,2.76,2.24,5,5,5s5-2.24,5-5V4C13,2.9,12.39,1.94,11.5,1.42z" />`;
|
||||
case MouseInputInteraction.Drag: return `
|
||||
<path class="dim" d="M8,7c0,0.55-0.45,1-1,1l0,0C6.45,8,6,7.55,6,7V4.5c0-0.55,0.45-1,1-1l0,0c0.55,0,1,0.45,1,1V7z" />
|
||||
<path class="bright" d="M11,16c-0.18,0-0.36-0.1-0.45-0.28c-0.12-0.25-0.02-0.55,0.22-0.67C10.87,15.01,13,13.88,13,11V6
|
||||
c0-0.28,0.22-0.5,0.5-0.5S14,5.72,14,6v5c0,3.52-2.66,4.89-2.78,4.95C11.15,15.98,11.08,16,11,16z" />
|
||||
<path class="bright" d="M14.5,15c-0.13,0-0.26-0.05-0.35-0.15c-0.19-0.19-0.2-0.51,0-0.7C14.17,14.12,15,13.2,15,11V8
|
||||
c0-0.28,0.22-0.5,0.5-0.5S16,7.72,16,8v3c0,2.68-1.1,3.81-1.15,3.85C14.76,14.95,14.63,15,14.5,15z" />
|
||||
<path class="dim" d="M9,2c1.1,0,2,0.9,2,2v6c0,2.21-1.79,4-4,4s-4-1.79-4-4V4c0-1.1,0.9-2,2-2H9 M9,1H5C3.35,1,2,2.35,2,4
|
||||
v6c0,2.76,2.24,5,5,5s5-2.24,5-5V4C12,2.35,10.65,1,9,1L9,1z" />`;
|
||||
case MouseInputInteraction.LMBDrag: return `
|
||||
<path class="bright" d="M11,16c-0.18,0-0.36-0.1-0.45-0.28c-0.12-0.25-0.02-0.55,0.22-0.67C10.87,15.01,13,13.88,13,11V6
|
||||
c0-0.28,0.22-0.5,0.5-0.5S14,5.72,14,6v5c0,3.52-2.66,4.89-2.78,4.95C11.15,15.98,11.08,16,11,16z" />
|
||||
<path class="bright" d="M14.5,15c-0.13,0-0.26-0.05-0.35-0.15c-0.19-0.19-0.2-0.51,0-0.7C14.17,14.12,15,13.2,15,11V8
|
||||
c0-0.28,0.22-0.5,0.5-0.5S16,7.72,16,8v3c0,2.68-1.1,3.81-1.15,3.85C14.76,14.95,14.63,15,14.5,15z" />
|
||||
<path class="bright" d="M7,1H5C3.35,1,2,2.35,2,4v4h5V1z" />
|
||||
<path class="dim" d="M9,1H8v1h1c1.1,0,2,0.9,2,2v6c0,2.21-1.79,4-4,4s-4-1.79-4-4V9H2v1c0,2.76,2.24,5,5,5s5-2.24,5-5V4
|
||||
C12,2.35,10.65,1,9,1z" />`;
|
||||
case MouseInputInteraction.RMBDrag: return `
|
||||
<path class="bright" d="M11,16c-0.18,0-0.36-0.1-0.45-0.28c-0.12-0.25-0.02-0.55,0.22-0.67C10.87,15.01,13,13.88,13,11V6
|
||||
c0-0.28,0.22-0.5,0.5-0.5S14,5.72,14,6v5c0,3.52-2.66,4.89-2.78,4.95C11.15,15.98,11.08,16,11,16z" />
|
||||
<path class="bright" d="M14.5,15c-0.13,0-0.26-0.05-0.35-0.15c-0.19-0.19-0.2-0.51,0-0.7C14.17,14.12,15,13.2,15,11V8
|
||||
c0-0.28,0.22-0.5,0.5-0.5S16,7.72,16,8v3c0,2.68-1.1,3.81-1.15,3.85C14.76,14.95,14.63,15,14.5,15z" />
|
||||
<path class="bright" d="M7,1h2c1.65,0,3,1.35,3,3v4H7V1z" />
|
||||
<path class="dim" d="M5,1h1v1H5C3.9,2,3,2.9,3,4v6c0,2.21,1.79,4,4,4s4-1.79,4-4V9h1v1c0,2.76-2.24,5-5,5s-5-2.24-5-5V4
|
||||
C2,2.35,3.35,1,5,1z" />`;
|
||||
case MouseInputInteraction.MMBDrag: return `
|
||||
<path class="bright" d="M8,7c0,0.55-0.45,1-1,1l0,0C6.45,8,6,7.55,6,7V4.5c0-0.55,0.45-1,1-1l0,0c0.55,0,1,0.45,1,1V7z" />
|
||||
<path class="bright" d="M11,16c-0.18,0-0.36-0.1-0.45-0.28c-0.12-0.25-0.02-0.55,0.22-0.67C10.87,15.01,13,13.88,13,11V6
|
||||
c0-0.28,0.22-0.5,0.5-0.5S14,5.72,14,6v5c0,3.52-2.66,4.89-2.78,4.95C11.15,15.98,11.08,16,11,16z" />
|
||||
<path class="bright" d="M14.5,15c-0.13,0-0.26-0.05-0.35-0.15c-0.19-0.19-0.2-0.51,0-0.7C14.17,14.12,15,13.2,15,11V8
|
||||
c0-0.28,0.22-0.5,0.5-0.5S16,7.72,16,8v3c0,2.68-1.1,3.81-1.15,3.85C14.76,14.95,14.63,15,14.5,15z" />
|
||||
<path class="dim" d="M9,2c1.1,0,2,0.9,2,2v6c0,2.21-1.79,4-4,4s-4-1.79-4-4V4c0-1.1,0.9-2,2-2H9 M9,1H5C3.35,1,2,2.35,2,4
|
||||
v6c0,2.76,2.24,5,5,5s5-2.24,5-5V4C12,2.35,10.65,1,9,1L9,1z" />`;
|
||||
default: return "";
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
59
client/web/src/components/window/title-bar/FileMenu.vue
Normal file
59
client/web/src/components/window/title-bar/FileMenu.vue
Normal file
@@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<div class="entry">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16">
|
||||
<path d="M5.5,10.2c0.1,0.2,0,0.3-0.1,0.4c-0.2,0.1-0.3,0-0.4-0.1c0,0,0,0,0,0L2.7,6.7c-0.1-0.2,0-0.3,0.1-0.4c0.2-0.1,0.3,0,0.4,0.1
|
||||
L5.5,10.2z M14.9,14.9L14.9,14.9C14.9,14.9,14.9,15,14.9,14.9C14.8,15,14.8,15,14.9,14.9L14.9,14.9c-0.5,0.2-1,0.4-1.5,0.5
|
||||
c-0.5,0.1-1.1,0.2-1.6,0.2c-0.5,0.1-1.1,0.1-1.6,0c-0.5,0-1.1-0.1-1.6-0.1l-1.6-0.1c-1.7,0-3.9-0.1-4.4-0.1s-1.1-0.2-1.1-0.3
|
||||
c0-0.1,0.3-0.2,0.6-0.2c0.5-0.1,1.1-0.2,1.7-0.3c0.6-0.1,2-0.2,4.1-0.2c0.8,0,1.8,0,1.8,0c0.6,0,1,0,1.9,0c0.7,0,1.1,0,1.2,0
|
||||
c0.3,0,0.6,0.1,0.9,0.2l-2.6-1.9c-0.2,0.1-0.4,0.2-0.7,0.2H4.5c-0.5,0-0.9-0.2-1.1-0.6l-2.9-5c-0.2-0.4-0.2-0.9,0-1.3l2.9-5
|
||||
C3.6,0.5,4,0.3,4.5,0.3h5.8c0.5,0,0.9,0.2,1.1,0.6l2.9,5l0,0l0,0c0,0.1,0.1,0.2,0.1,0.3c0,0,0,0.1,0,0.1c0,0.1,0,0.1,0,0.2
|
||||
c0,0,0.7,7.2,0.7,7.8v0C15.2,14.6,15.1,14.8,14.9,14.9L14.9,14.9z M4.3,1.9L4.2,2l3.6,6.2c0.4-0.1,0.9-0.2,1.4-0.2l0.9-1.3l1.6-0.2
|
||||
c0.2-0.4,0.5-0.8,0.8-1.1l-2.1-3.7c-0.1-0.2-0.3-0.3-0.5-0.3H4.8C4.6,1.6,4.4,1.7,4.3,1.9z M9.4,11.6l-2-1.4C7.4,10.1,7.3,10,7.3,10
|
||||
l0,0L3.5,3.3l-1.7,3c-0.1,0.2-0.1,0.4,0,0.6l2.6,4.5c0.1,0.2,0.3,0.3,0.5,0.3L9.4,11.6z M13.5,11.3l-0.5-4.6c-0.3,0.3-0.5,0.7-0.7,1
|
||||
l-1.6,0.2L9.8,9.2c-0.4,0-0.8,0-1.3,0.1l3.7,2.7C12.5,11.6,13,11.3,13.5,11.3L13.5,11.3z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="entry"><span>File</span></div>
|
||||
<div class="entry"><span>Edit</span></div>
|
||||
<div class="entry"><span>Document</span></div>
|
||||
<div class="entry"><span>View</span></div>
|
||||
<div class="entry"><span>Help</span></div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.entry {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
padding: 0 8px;
|
||||
|
||||
svg {
|
||||
fill: #ddd;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: #555;
|
||||
|
||||
svg {
|
||||
fill: #fff;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
import { ApplicationPlatform } from "../MainWindow.vue";
|
||||
|
||||
export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
ApplicationPlatform,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
58
client/web/src/components/window/title-bar/TitleBar.vue
Normal file
58
client/web/src/components/window/title-bar/TitleBar.vue
Normal file
@@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<div class="header-third">
|
||||
<WindowButtonsMac :maximized="maximized" v-if="platform === ApplicationPlatform.Mac" />
|
||||
<FileMenu v-if="platform !== ApplicationPlatform.Mac" />
|
||||
</div>
|
||||
<div class="header-third">
|
||||
<WindowTitle :title="'X-35B Over Death Valley.gdd* - Graphite'" />
|
||||
</div>
|
||||
<div class="header-third">
|
||||
<WindowButtonsWindows :maximized="maximized" v-if="platform === ApplicationPlatform.Windows || platform === ApplicationPlatform.Linux" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.header-third {
|
||||
display: flex;
|
||||
flex: 1 1 100%;
|
||||
|
||||
&:nth-child(1) {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
&:nth-child(3) {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
import FileMenu from "./FileMenu.vue";
|
||||
import WindowTitle from "./WindowTitle.vue";
|
||||
import WindowButtonsWindows from "./WindowButtonsWindows.vue";
|
||||
import WindowButtonsMac from "./WindowButtonsMac.vue";
|
||||
import { ApplicationPlatform } from "../MainWindow.vue";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
FileMenu,
|
||||
WindowTitle,
|
||||
WindowButtonsWindows,
|
||||
WindowButtonsMac,
|
||||
},
|
||||
props: {
|
||||
platform: { type: String, required: true },
|
||||
maximized: { type: Boolean, required: true },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
ApplicationPlatform,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<div class="mac window-buttons">
|
||||
<div class="close" title="Close"></div>
|
||||
<div class="minimize" title="Minimize"></div>
|
||||
<div class="zoom" title="Zoom"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.mac.window-buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: 8px;
|
||||
width: 11px;
|
||||
height: 11px;
|
||||
border-radius: 50%;
|
||||
|
||||
&.close {
|
||||
background: #ff5f57;
|
||||
}
|
||||
|
||||
&.minimize {
|
||||
background: #ffbd2f;
|
||||
}
|
||||
|
||||
&.zoom {
|
||||
background: #29c93f;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
maximized: { type: Boolean, default: false },
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<div class="windows window-button minimize" title="Minimize">
|
||||
<svg width="10" height="10" viewBox="0 0 10 10">
|
||||
<rect y="4" width="10" height="1" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="windows window-button maximize" title="Maximize" v-if="!maximized">
|
||||
<svg width="10" height="10" viewBox="0 0 10 10">
|
||||
<path d="M10,0v10H0V0H10z M9,1H1v8h8V1z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="windows window-button restore-down" title="Restore Down" v-if="maximized">
|
||||
<svg width="10" height="10" viewBox="0 0 10 10">
|
||||
<path d="M10,8H8v2H0V2h2V0h8V8z M7,3H1v6h6V3z M9,1H3v1h5v5h1V1z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="windows window-button close" title="Close">
|
||||
<svg width="10" height="10" viewBox="0 0 10 10">
|
||||
<polygon points="10,0.7 9.3,0 5,4.3 0.7,0 0,0.7 4.3,5 0,9.3 0.7,10 5,5.7 9.3,10 10,9.3 5.7,5" />
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.windows.window-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 20px;
|
||||
|
||||
svg {
|
||||
fill: #ddd;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: #555;
|
||||
|
||||
svg {
|
||||
fill: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
&.close:hover {
|
||||
background: #e81123;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
maximized: { type: Boolean, default: false },
|
||||
},
|
||||
});
|
||||
</script>
|
||||
24
client/web/src/components/window/title-bar/WindowTitle.vue
Normal file
24
client/web/src/components/window/title-bar/WindowTitle.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<template>
|
||||
<div class="window-title">
|
||||
<span>{{title}}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.window-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
padding: 0 8px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
title: { type: String, required: true },
|
||||
},
|
||||
});
|
||||
</script>
|
||||
Reference in New Issue
Block a user