* Move Elsewhen into Omarchy as omarchy.elsewhen The world clock ships in the shell tree instead of its own package. A migration renames existing bar entries with their settings and removes the retired package. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Remove Elsewhen's shelved Earth row, overlap band and sky tint Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Tidy Elsewhen's models, data helper and docs, and drop currency Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Restructure Elsewhen's panel and globe on the shared shell components Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Show Elsewhen's bar globe upright Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Use the shared city search for Elsewhen's globe jump bar Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep legacy Elsewhen entries single and retry offline geocodes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fetch Elsewhen's weather with curl instead of a Python helper Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
165 lines
4.4 KiB
QML
165 lines
4.4 KiB
QML
import QtQuick
|
|
import qs.Commons
|
|
import "GlobeModel.js" as Solar
|
|
|
|
// "World [globe] Clock" under an arched caption. The little globe is the door
|
|
// into globe mode and, once the big globe has left it, the cross back out.
|
|
Column {
|
|
id: hero
|
|
|
|
property string caption: ""
|
|
property color captionColor: dim
|
|
property bool captionClickable: false
|
|
property color foreground: Color.foreground
|
|
property color dim: Qt.darker(foreground, 1.55)
|
|
property string fontFamily: Style.font.family
|
|
|
|
// 0 is the list, 1 is the globe filling the panel.
|
|
property real zoom: 0
|
|
property bool globeEnabled: true
|
|
property real bigGlobeOpacity: 0
|
|
|
|
// Where the little globe rests, and the city it marks.
|
|
property real restLon: 0
|
|
property bool markerShown: false
|
|
property real markerLat: 0
|
|
property real markerLon: 0
|
|
|
|
readonly property Item icon: heroIcon
|
|
readonly property bool iconHovered: heroMouse.containsMouse
|
|
readonly property real spin: heroIcon.spin
|
|
|
|
signal captionClicked()
|
|
signal globeClicked(bool slow)
|
|
|
|
// Three turns that land on `lon`; null replays the last endpoints.
|
|
function spinOpening(lon) {
|
|
globeSpin.stop()
|
|
if (lon !== null) {
|
|
globeSpin.from = lon - 1080
|
|
globeSpin.to = lon
|
|
}
|
|
globeSpin.restart()
|
|
}
|
|
|
|
// `from` is passed in: once the focus changes, the rest binding has already moved spin.
|
|
function turn(from, lon) {
|
|
globeSpin.stop()
|
|
focusSpin.from = from
|
|
focusSpin.to = from + Solar.shortestTurn(from, lon)
|
|
focusSpin.restart()
|
|
}
|
|
|
|
spacing: Style.space(2)
|
|
|
|
ArcText {
|
|
width: parent.width
|
|
text: hero.caption
|
|
rise: Style.space(6)
|
|
color: hero.captionColor
|
|
fontFamily: hero.fontFamily
|
|
pixelSize: Style.font.caption
|
|
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
enabled: hero.captionClickable
|
|
cursorShape: Qt.PointingHandCursor
|
|
onClicked: hero.captionClicked()
|
|
}
|
|
}
|
|
|
|
Row {
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
spacing: Style.space(9)
|
|
|
|
TitleWord { text: "World" }
|
|
|
|
Item {
|
|
id: heroIcon
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
implicitWidth: Math.round(Style.font.display * 1.3)
|
|
implicitHeight: Math.round(Style.font.display * 1.3)
|
|
|
|
// Degrees of longitude facing the viewer; the globe draws the spin itself.
|
|
property real spin: 0
|
|
|
|
Binding {
|
|
target: heroIcon
|
|
property: "spin"
|
|
value: hero.restLon
|
|
when: !globeSpin.running && !focusSpin.running
|
|
restoreMode: Binding.RestoreNone
|
|
}
|
|
|
|
rotation: Solar.AXIAL_TILT
|
|
|
|
Text {
|
|
anchors.centerIn: parent
|
|
textFormat: Text.PlainText
|
|
text: "\u00d7"
|
|
color: heroMouse.containsMouse ? hero.foreground : hero.dim
|
|
font.family: hero.fontFamily
|
|
font.pixelSize: Math.round(heroIcon.width * 0.8)
|
|
// The globe leans with the earth; the cross stays upright.
|
|
rotation: -Solar.AXIAL_TILT
|
|
opacity: Util.clamp((hero.zoom - 0.2) / 0.35, 0, 1)
|
|
visible: opacity > 0
|
|
}
|
|
|
|
MiniGlobe {
|
|
anchors.fill: parent
|
|
opacity: 1 - hero.bigGlobeOpacity
|
|
visible: opacity > 0
|
|
spin: heroIcon.spin
|
|
// Full strength: at this size a dimmed globe reads as washed out.
|
|
color: hero.foreground
|
|
bold: true
|
|
showMarker: hero.markerShown
|
|
markerLat: hero.markerLat
|
|
markerLon: hero.markerLon
|
|
}
|
|
|
|
// A MouseArea rather than a TapHandler: only mouse events carry the
|
|
// Shift modifier that asks for slow motion.
|
|
MouseArea {
|
|
id: heroMouse
|
|
anchors.fill: parent
|
|
enabled: hero.globeEnabled
|
|
hoverEnabled: true
|
|
acceptedButtons: Qt.LeftButton
|
|
cursorShape: Qt.PointingHandCursor
|
|
onClicked: function(mouse) {
|
|
hero.globeClicked((mouse.modifiers & Qt.ShiftModifier) !== 0)
|
|
}
|
|
}
|
|
|
|
NumberAnimation {
|
|
id: globeSpin
|
|
target: heroIcon
|
|
property: "spin"
|
|
duration: 1250
|
|
easing.type: Easing.OutQuart
|
|
}
|
|
|
|
NumberAnimation {
|
|
id: focusSpin
|
|
target: heroIcon
|
|
property: "spin"
|
|
duration: 700
|
|
easing.type: Easing.OutCubic
|
|
}
|
|
}
|
|
|
|
TitleWord { text: "Clock" }
|
|
}
|
|
|
|
component TitleWord: Text {
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
textFormat: Text.PlainText
|
|
color: hero.foreground
|
|
font.family: hero.fontFamily
|
|
font.pixelSize: Style.font.title
|
|
font.weight: Font.DemiBold
|
|
}
|
|
}
|