Order displays based on logical position
This commit is contained in:
parent
f6dbb37a02
commit
1ba6c8a026
4 changed files with 228 additions and 180 deletions
|
|
@ -9,104 +9,98 @@ class Bar extends ConsumerWidget {
|
|||
const Bar({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) => Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 8),
|
||||
child: Stack(
|
||||
children:
|
||||
[
|
||||
ref
|
||||
.watch(WorkspacesController.provider)
|
||||
.when(
|
||||
error: (error, stackTrace) => [Text(error.toString())],
|
||||
loading: () => [SizedBox.shrink()],
|
||||
data: (value) => value
|
||||
.map(
|
||||
(group) => Row(
|
||||
children: group
|
||||
.map(
|
||||
(workspace) => IconButton(
|
||||
onPressed: () => workspace.activate(),
|
||||
icon: Icon(
|
||||
workspace.activated
|
||||
? Icons.circle
|
||||
: Icons.circle_outlined,
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
[
|
||||
TextButton(
|
||||
onPressed: () {},
|
||||
child: Text(
|
||||
DateFormat.Hm().format(
|
||||
ref
|
||||
.watch(TimeController.provider)
|
||||
.when(
|
||||
data: (time) => time,
|
||||
loading: DateTime.now,
|
||||
error: (_, _) => DateTime.now(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
[
|
||||
Row(
|
||||
children: [
|
||||
IconButton(onPressed: () {}, icon: Icon(Icons.wifi)),
|
||||
IconButton(
|
||||
onPressed: () {},
|
||||
icon: Icon(Icons.bluetooth),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {},
|
||||
icon: Icon(Icons.volume_off),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
]
|
||||
.mapIndexed(
|
||||
(index, children) => Align(
|
||||
alignment: [
|
||||
Alignment.centerLeft,
|
||||
Alignment.center,
|
||||
Alignment.centerRight,
|
||||
][index],
|
||||
child: Row(
|
||||
spacing: 8,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: children
|
||||
Widget build(BuildContext context, WidgetRef ref) => Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 8),
|
||||
child: Stack(
|
||||
children:
|
||||
[
|
||||
ref
|
||||
.watch(WorkspacesController.provider)
|
||||
.when(
|
||||
error: (error, stackTrace) => [Text(error.toString())],
|
||||
loading: () => [SizedBox.shrink()],
|
||||
data: (value) => value
|
||||
.map(
|
||||
(child) => Padding(
|
||||
padding: EdgeInsetsGeometry.directional(
|
||||
bottom: 6,
|
||||
),
|
||||
child: Container(
|
||||
height: 42,
|
||||
padding: EdgeInsets.symmetric(horizontal: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.surfaceContainerLow,
|
||||
borderRadius: BorderRadius.circular(999),
|
||||
),
|
||||
child: child,
|
||||
),
|
||||
(group) => Row(
|
||||
children: group
|
||||
.map(
|
||||
(workspace) => IconButton(
|
||||
onPressed: () => workspace.activate(),
|
||||
icon: Icon(
|
||||
workspace.activated
|
||||
? Icons.circle
|
||||
: Icons.circle_outlined,
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
[
|
||||
TextButton(
|
||||
onPressed: () {},
|
||||
child: Text(
|
||||
DateFormat.Hm().format(
|
||||
ref
|
||||
.watch(TimeController.provider)
|
||||
.when(
|
||||
data: (time) => time,
|
||||
loading: DateTime.now,
|
||||
error: (_, _) => DateTime.now(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
],
|
||||
|
||||
[
|
||||
Row(
|
||||
children: [
|
||||
IconButton(onPressed: () {}, icon: Icon(Icons.wifi)),
|
||||
IconButton(onPressed: () {}, icon: Icon(Icons.bluetooth)),
|
||||
|
||||
IconButton(
|
||||
onPressed: () {},
|
||||
icon: Icon(Icons.volume_off),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
]
|
||||
.mapIndexed(
|
||||
(index, children) => Align(
|
||||
alignment: [
|
||||
Alignment.centerLeft,
|
||||
Alignment.center,
|
||||
Alignment.centerRight,
|
||||
][index],
|
||||
child: Row(
|
||||
spacing: 8,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: children
|
||||
.map(
|
||||
(child) => Padding(
|
||||
padding: EdgeInsetsGeometry.directional(bottom: 6),
|
||||
child: Container(
|
||||
height: 42,
|
||||
padding: EdgeInsets.symmetric(horizontal: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.surfaceContainerLow,
|
||||
borderRadius: BorderRadius.circular(999),
|
||||
),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ serde = { version = "1", features = ["derive"] }
|
|||
serde_json = "1"
|
||||
|
||||
wayland-client = "0.31"
|
||||
wayland-protocols = { version = "0.32.12", features = ["client", "staging"] }
|
||||
wayland-protocols = { version = "0.32.12", features = ["client", "staging", "unstable"] }
|
||||
|
||||
calloop = "0.14.4"
|
||||
calloop-wayland-source = "0.4.1"
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
use crate::{frb_generated::StreamSink, internal::wayland::AppState};
|
||||
use std::{collections::HashMap, sync::{Mutex, OnceLock}};
|
||||
use serde_json::Result;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
sync::{Mutex, OnceLock},
|
||||
};
|
||||
|
||||
use calloop::{
|
||||
channel::{channel, Channel, Sender},
|
||||
EventLoop,
|
||||
channel::{Channel, Sender, channel},
|
||||
};
|
||||
use calloop_wayland_source::WaylandSource;
|
||||
|
||||
|
|
@ -25,8 +28,8 @@ pub struct Workspace {
|
|||
impl Workspace {
|
||||
pub fn activate(&self) {
|
||||
if let Some(tx) = controller().lock().unwrap().as_ref() {
|
||||
let _ = tx.send(self.id);
|
||||
}
|
||||
let _ = tx.send(self.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -39,10 +42,11 @@ pub fn listen_workspaces(sink: StreamSink<Vec<Vec<Workspace>>>) -> Result<()> {
|
|||
let qh = event_queue.handle();
|
||||
|
||||
let mut state = AppState {
|
||||
outputs: HashMap::new(),
|
||||
outputs: HashMap::new(),
|
||||
workspaces: HashMap::new(),
|
||||
workspace_groups: HashMap::new(),
|
||||
workspace_handles: HashMap::new(),
|
||||
xdg_output_manager: None,
|
||||
sink,
|
||||
};
|
||||
|
||||
|
|
@ -69,4 +73,4 @@ pub fn listen_workspaces(sink: StreamSink<Vec<Vec<Workspace>>>) -> Result<()> {
|
|||
while let Ok(_) = event_loop.dispatch(None, &mut state) {}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,16 @@ use crate::{frb_generated::StreamSink, workspace_api::Workspace};
|
|||
use std::collections::HashMap;
|
||||
|
||||
use wayland_client::{
|
||||
self, event_created_child, protocol::{wl_registry, wl_output}, Connection, Dispatch, Proxy, QueueHandle,
|
||||
self, event_created_child,
|
||||
protocol::{wl_output, wl_registry},
|
||||
Connection, Dispatch, Proxy, QueueHandle,
|
||||
};
|
||||
|
||||
use wayland_protocols::ext::workspace::v1::client::{
|
||||
ext_workspace_group_handle_v1, ext_workspace_handle_v1, ext_workspace_manager_v1,
|
||||
use wayland_protocols::{
|
||||
ext::workspace::v1::client::{
|
||||
ext_workspace_group_handle_v1, ext_workspace_handle_v1, ext_workspace_manager_v1,
|
||||
},
|
||||
xdg::xdg_output::zv1::client::{zxdg_output_manager_v1, zxdg_output_v1},
|
||||
};
|
||||
|
||||
pub struct WorkspaceHandles {
|
||||
|
|
@ -29,34 +34,34 @@ pub struct AppState {
|
|||
pub workspace_groups: HashMap<u32, WorkspaceGroup>,
|
||||
pub workspace_handles: HashMap<u32, WorkspaceHandles>,
|
||||
pub sink: StreamSink<Vec<Vec<Workspace>>>,
|
||||
pub xdg_output_manager: Option<zxdg_output_manager_v1::ZxdgOutputManagerV1>,
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
fn emit(&self) {
|
||||
let mut groups: Vec<_> = self.workspace_groups.values().collect();
|
||||
let mut groups: Vec<_> = self.workspace_groups.values().collect();
|
||||
|
||||
groups.sort_by_key(|group| {
|
||||
group.output_id.as_ref().and_then(|o| {
|
||||
self.outputs.get(o).map(|info| info.coords.0)
|
||||
})
|
||||
});
|
||||
groups.sort_by_key(|group| {
|
||||
group
|
||||
.output_id
|
||||
.as_ref()
|
||||
.and_then(|o| self.outputs.get(o).map(|info| info.coords.0))
|
||||
});
|
||||
|
||||
let result: Vec<Vec<Workspace>> = groups
|
||||
.into_iter()
|
||||
.map(|group| {
|
||||
let mut workspaces: Vec<Workspace> = group
|
||||
.children
|
||||
.iter()
|
||||
.filter_map(|id| self.workspaces.get(id))
|
||||
.cloned()
|
||||
.collect();
|
||||
.into_iter()
|
||||
.map(|group| {
|
||||
let mut workspaces: Vec<Workspace> = group
|
||||
.children
|
||||
.iter()
|
||||
.filter_map(|id| self.workspaces.get(id))
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
workspaces.sort_by_key(|ws|
|
||||
ws.coords.as_ref().map(|coords| coords.0)
|
||||
);
|
||||
workspaces
|
||||
})
|
||||
.collect();
|
||||
workspaces.sort_by_key(|ws| ws.coords.as_ref().map(|coords| coords.0));
|
||||
workspaces
|
||||
})
|
||||
.collect();
|
||||
|
||||
self.sink.add(result).expect("Updating stream failed");
|
||||
}
|
||||
|
|
@ -64,7 +69,7 @@ impl AppState {
|
|||
|
||||
impl Dispatch<wl_registry::WlRegistry, ()> for AppState {
|
||||
fn event(
|
||||
_state: &mut Self,
|
||||
state: &mut Self,
|
||||
registry: &wl_registry::WlRegistry,
|
||||
event: wl_registry::Event,
|
||||
_data: &(),
|
||||
|
|
@ -77,27 +82,34 @@ impl Dispatch<wl_registry::WlRegistry, ()> for AppState {
|
|||
version,
|
||||
} = event
|
||||
{
|
||||
match interface.as_str() {
|
||||
"ext_workspace_manager_v1" => {
|
||||
registry.bind::<ext_workspace_manager_v1::ExtWorkspaceManagerV1, (), AppState>(
|
||||
name,
|
||||
version,
|
||||
qh,
|
||||
(),
|
||||
);
|
||||
}
|
||||
match interface.as_str() {
|
||||
"ext_workspace_manager_v1" => {
|
||||
registry.bind::<ext_workspace_manager_v1::ExtWorkspaceManagerV1, (), AppState>(
|
||||
name,
|
||||
version,
|
||||
qh,
|
||||
(),
|
||||
);
|
||||
}
|
||||
|
||||
"wl_output" => {
|
||||
registry.bind::<wl_output::WlOutput, (), AppState>(
|
||||
name,
|
||||
version,
|
||||
qh,
|
||||
(),
|
||||
);
|
||||
}
|
||||
"zxdg_output_manager_v1" => {
|
||||
let manager: zxdg_output_manager_v1::ZxdgOutputManagerV1 =
|
||||
registry.bind::<zxdg_output_manager_v1::ZxdgOutputManagerV1, (), AppState>(
|
||||
name,
|
||||
version,
|
||||
qh,
|
||||
(),
|
||||
);
|
||||
|
||||
_ => {}
|
||||
}
|
||||
state.xdg_output_manager = Some(manager);
|
||||
}
|
||||
|
||||
"wl_output" => {
|
||||
registry.bind::<wl_output::WlOutput, (), AppState>(name, version, qh, ());
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -117,18 +129,18 @@ impl Dispatch<ext_workspace_manager_v1::ExtWorkspaceManagerV1, ()> for AppState
|
|||
workspace.id().protocol_id(),
|
||||
Workspace {
|
||||
activated: false,
|
||||
id: workspace.id().protocol_id(),
|
||||
id: workspace.id().protocol_id(),
|
||||
coords: None,
|
||||
},
|
||||
);
|
||||
|
||||
state.workspace_handles.insert(
|
||||
workspace.id().protocol_id(),
|
||||
WorkspaceHandles {
|
||||
handle: workspace,
|
||||
manager_handle: proxy.clone(),
|
||||
}
|
||||
);
|
||||
state.workspace_handles.insert(
|
||||
workspace.id().protocol_id(),
|
||||
WorkspaceHandles {
|
||||
handle: workspace,
|
||||
manager_handle: proxy.clone(),
|
||||
},
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
@ -207,7 +219,7 @@ impl Dispatch<ext_workspace_group_handle_v1::ExtWorkspaceGroupHandleV1, ()> for
|
|||
event: ext_workspace_group_handle_v1::Event,
|
||||
_data: &(),
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
qh: &QueueHandle<Self>,
|
||||
) {
|
||||
let group_id = proxy.id();
|
||||
|
||||
|
|
@ -215,26 +227,30 @@ impl Dispatch<ext_workspace_group_handle_v1::ExtWorkspaceGroupHandleV1, ()> for
|
|||
ext_workspace_group_handle_v1::Event::WorkspaceEnter { workspace } => {
|
||||
state
|
||||
.workspace_groups
|
||||
.entry(group_id.protocol_id())
|
||||
.or_insert_with(|| WorkspaceGroup {
|
||||
output_id: None,
|
||||
children: Vec::new(),
|
||||
})
|
||||
.children
|
||||
.push(workspace.id().protocol_id());
|
||||
.entry(group_id.protocol_id())
|
||||
.or_insert_with(|| WorkspaceGroup {
|
||||
output_id: None,
|
||||
children: Vec::new(),
|
||||
})
|
||||
.children
|
||||
.push(workspace.id().protocol_id());
|
||||
state.emit();
|
||||
}
|
||||
|
||||
ext_workspace_group_handle_v1::Event::OutputEnter { output } => {
|
||||
state
|
||||
.workspace_groups
|
||||
.entry(group_id.protocol_id())
|
||||
.or_insert_with(|| WorkspaceGroup {
|
||||
output_id: None,
|
||||
children: Vec::new(),
|
||||
})
|
||||
.output_id = Some(output.id().protocol_id());
|
||||
state.emit();
|
||||
ext_workspace_group_handle_v1::Event::OutputEnter { output } => {
|
||||
if let Some(manager) = &state.xdg_output_manager {
|
||||
let xdg_output = manager.get_xdg_output(&output, qh, ());
|
||||
state
|
||||
.workspace_groups
|
||||
.entry(group_id.protocol_id())
|
||||
.or_insert_with(|| WorkspaceGroup {
|
||||
output_id: None,
|
||||
children: Vec::new(),
|
||||
})
|
||||
.output_id = Some(xdg_output.id().protocol_id());
|
||||
|
||||
state.emit();
|
||||
}
|
||||
}
|
||||
|
||||
ext_workspace_group_handle_v1::Event::WorkspaceLeave { workspace } => {
|
||||
|
|
@ -247,25 +263,59 @@ impl Dispatch<ext_workspace_group_handle_v1::ExtWorkspaceGroupHandleV1, ()> for
|
|||
}
|
||||
}
|
||||
|
||||
impl Dispatch<wl_output::WlOutput, ()> for AppState {
|
||||
impl Dispatch<zxdg_output_manager_v1::ZxdgOutputManagerV1, ()> for AppState {
|
||||
fn event(
|
||||
_state: &mut Self,
|
||||
_proxy: &zxdg_output_manager_v1::ZxdgOutputManagerV1,
|
||||
_event: <zxdg_output_manager_v1::ZxdgOutputManagerV1 as Proxy>::Event,
|
||||
_data: &(),
|
||||
_conn: &Connection,
|
||||
_qhandle: &QueueHandle<Self>,
|
||||
) {
|
||||
// Do nothing, for now
|
||||
}
|
||||
|
||||
event_created_child!(
|
||||
AppState,
|
||||
zxdg_output_manager_v1::ZxdgOutputManagerV1,
|
||||
[
|
||||
zxdg_output_manager_v1::REQ_GET_XDG_OUTPUT_OPCODE => (
|
||||
zxdg_output_v1::ZxdgOutputV1,
|
||||
()
|
||||
)
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
impl Dispatch<zxdg_output_v1::ZxdgOutputV1, ()> for AppState {
|
||||
fn event(
|
||||
state: &mut Self,
|
||||
proxy: &wl_output::WlOutput,
|
||||
event: wl_output::Event,
|
||||
proxy: &zxdg_output_v1::ZxdgOutputV1,
|
||||
event: zxdg_output_v1::Event,
|
||||
_data: &(),
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
) {
|
||||
match event {
|
||||
wl_output::Event::Geometry {
|
||||
x,
|
||||
y,
|
||||
..
|
||||
} => {
|
||||
state.outputs.insert(proxy.id().protocol_id(), Output { coords: ( x, y ) });
|
||||
zxdg_output_v1::Event::LogicalPosition { x, y } => {
|
||||
state
|
||||
.outputs
|
||||
.insert(proxy.id().protocol_id(), Output { coords: (x, y) });
|
||||
state.emit();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Dispatch<wl_output::WlOutput, ()> for AppState {
|
||||
fn event(
|
||||
_state: &mut Self,
|
||||
_proxy: &wl_output::WlOutput,
|
||||
_event: wl_output::Event,
|
||||
_data: &(),
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
) {
|
||||
// Do nothing, just bind
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue