mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-07 20:39:04 +03:00
WIP - start work on keyboard navigation in contacts panel
This commit is contained in:
parent
297fa1af55
commit
08a7543913
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -936,9 +936,11 @@ dependencies = [
|
||||
"futures",
|
||||
"fuzzy",
|
||||
"gpui",
|
||||
"language",
|
||||
"log",
|
||||
"picker",
|
||||
"postage",
|
||||
"project",
|
||||
"serde",
|
||||
"settings",
|
||||
"theme",
|
||||
|
@ -1249,20 +1249,29 @@
|
||||
"button_width": 8,
|
||||
"icon_width": 8
|
||||
},
|
||||
"row": {
|
||||
"padding": {
|
||||
"left": 8
|
||||
}
|
||||
},
|
||||
"row_height": 28,
|
||||
"header": {
|
||||
"header_row": {
|
||||
"family": "Zed Mono",
|
||||
"color": "#8b8792",
|
||||
"size": 14,
|
||||
"margin": {
|
||||
"top": 8
|
||||
},
|
||||
"active": {
|
||||
"family": "Zed Mono",
|
||||
"color": "#e2dfe7",
|
||||
"size": 14,
|
||||
"background": "#5852605c"
|
||||
}
|
||||
},
|
||||
"contact_row": {
|
||||
"padding": {
|
||||
"left": 8
|
||||
},
|
||||
"active": {
|
||||
"background": "#5852605c"
|
||||
}
|
||||
},
|
||||
"row_height": 28,
|
||||
"tree_branch_color": "#655f6d",
|
||||
"tree_branch_width": 1,
|
||||
"contact_avatar": {
|
||||
@ -1294,26 +1303,7 @@
|
||||
"button_width": 16,
|
||||
"corner_radius": 8
|
||||
},
|
||||
"project": {
|
||||
"guest_avatar_spacing": 4,
|
||||
"height": 24,
|
||||
"guest_avatar": {
|
||||
"corner_radius": 8,
|
||||
"width": 14
|
||||
},
|
||||
"name": {
|
||||
"family": "Zed Mono",
|
||||
"color": "#7e7887",
|
||||
"size": 14,
|
||||
"margin": {
|
||||
"right": 6
|
||||
}
|
||||
},
|
||||
"padding": {
|
||||
"left": 8
|
||||
}
|
||||
},
|
||||
"shared_project": {
|
||||
"shared_project_row": {
|
||||
"guest_avatar_spacing": 4,
|
||||
"height": 24,
|
||||
"guest_avatar": {
|
||||
@ -1332,9 +1322,15 @@
|
||||
"left": 8
|
||||
},
|
||||
"background": "#26232a",
|
||||
"corner_radius": 6
|
||||
"corner_radius": 6,
|
||||
"hover": {
|
||||
"background": "#5852603d"
|
||||
},
|
||||
"active": {
|
||||
"background": "#5852605c"
|
||||
}
|
||||
},
|
||||
"hovered_shared_project": {
|
||||
"unshared_project_row": {
|
||||
"guest_avatar_spacing": 4,
|
||||
"height": 24,
|
||||
"guest_avatar": {
|
||||
@ -1352,47 +1348,14 @@
|
||||
"padding": {
|
||||
"left": 8
|
||||
},
|
||||
"background": "#5852603d",
|
||||
"corner_radius": 6
|
||||
},
|
||||
"unshared_project": {
|
||||
"guest_avatar_spacing": 4,
|
||||
"height": 24,
|
||||
"guest_avatar": {
|
||||
"corner_radius": 8,
|
||||
"width": 14
|
||||
"background": "#26232a",
|
||||
"corner_radius": 6,
|
||||
"hover": {
|
||||
"background": "#5852603d"
|
||||
},
|
||||
"name": {
|
||||
"family": "Zed Mono",
|
||||
"color": "#7e7887",
|
||||
"size": 14,
|
||||
"margin": {
|
||||
"right": 6
|
||||
}
|
||||
},
|
||||
"padding": {
|
||||
"left": 8
|
||||
"active": {
|
||||
"background": "#5852605c"
|
||||
}
|
||||
},
|
||||
"hovered_unshared_project": {
|
||||
"guest_avatar_spacing": 4,
|
||||
"height": 24,
|
||||
"guest_avatar": {
|
||||
"corner_radius": 8,
|
||||
"width": 14
|
||||
},
|
||||
"name": {
|
||||
"family": "Zed Mono",
|
||||
"color": "#7e7887",
|
||||
"size": 14,
|
||||
"margin": {
|
||||
"right": 6
|
||||
}
|
||||
},
|
||||
"padding": {
|
||||
"left": 8
|
||||
},
|
||||
"corner_radius": 6
|
||||
}
|
||||
},
|
||||
"contact_finder": {
|
||||
|
@ -1249,20 +1249,29 @@
|
||||
"button_width": 8,
|
||||
"icon_width": 8
|
||||
},
|
||||
"row": {
|
||||
"padding": {
|
||||
"left": 8
|
||||
}
|
||||
},
|
||||
"row_height": 28,
|
||||
"header": {
|
||||
"header_row": {
|
||||
"family": "Zed Mono",
|
||||
"color": "#585260",
|
||||
"size": 14,
|
||||
"margin": {
|
||||
"top": 8
|
||||
},
|
||||
"active": {
|
||||
"family": "Zed Mono",
|
||||
"color": "#26232a",
|
||||
"size": 14,
|
||||
"background": "#8b87922e"
|
||||
}
|
||||
},
|
||||
"contact_row": {
|
||||
"padding": {
|
||||
"left": 8
|
||||
},
|
||||
"active": {
|
||||
"background": "#8b87922e"
|
||||
}
|
||||
},
|
||||
"row_height": 28,
|
||||
"tree_branch_color": "#7e7887",
|
||||
"tree_branch_width": 1,
|
||||
"contact_avatar": {
|
||||
@ -1294,26 +1303,7 @@
|
||||
"button_width": 16,
|
||||
"corner_radius": 8
|
||||
},
|
||||
"project": {
|
||||
"guest_avatar_spacing": 4,
|
||||
"height": 24,
|
||||
"guest_avatar": {
|
||||
"corner_radius": 8,
|
||||
"width": 14
|
||||
},
|
||||
"name": {
|
||||
"family": "Zed Mono",
|
||||
"color": "#655f6d",
|
||||
"size": 14,
|
||||
"margin": {
|
||||
"right": 6
|
||||
}
|
||||
},
|
||||
"padding": {
|
||||
"left": 8
|
||||
}
|
||||
},
|
||||
"shared_project": {
|
||||
"shared_project_row": {
|
||||
"guest_avatar_spacing": 4,
|
||||
"height": 24,
|
||||
"guest_avatar": {
|
||||
@ -1332,9 +1322,15 @@
|
||||
"left": 8
|
||||
},
|
||||
"background": "#e2dfe7",
|
||||
"corner_radius": 6
|
||||
"corner_radius": 6,
|
||||
"hover": {
|
||||
"background": "#8b87921f"
|
||||
},
|
||||
"active": {
|
||||
"background": "#8b87922e"
|
||||
}
|
||||
},
|
||||
"hovered_shared_project": {
|
||||
"unshared_project_row": {
|
||||
"guest_avatar_spacing": 4,
|
||||
"height": 24,
|
||||
"guest_avatar": {
|
||||
@ -1352,47 +1348,14 @@
|
||||
"padding": {
|
||||
"left": 8
|
||||
},
|
||||
"background": "#8b87921f",
|
||||
"corner_radius": 6
|
||||
},
|
||||
"unshared_project": {
|
||||
"guest_avatar_spacing": 4,
|
||||
"height": 24,
|
||||
"guest_avatar": {
|
||||
"corner_radius": 8,
|
||||
"width": 14
|
||||
"background": "#e2dfe7",
|
||||
"corner_radius": 6,
|
||||
"hover": {
|
||||
"background": "#8b87921f"
|
||||
},
|
||||
"name": {
|
||||
"family": "Zed Mono",
|
||||
"color": "#655f6d",
|
||||
"size": 14,
|
||||
"margin": {
|
||||
"right": 6
|
||||
}
|
||||
},
|
||||
"padding": {
|
||||
"left": 8
|
||||
"active": {
|
||||
"background": "#8b87922e"
|
||||
}
|
||||
},
|
||||
"hovered_unshared_project": {
|
||||
"guest_avatar_spacing": 4,
|
||||
"height": 24,
|
||||
"guest_avatar": {
|
||||
"corner_radius": 8,
|
||||
"width": 14
|
||||
},
|
||||
"name": {
|
||||
"family": "Zed Mono",
|
||||
"color": "#655f6d",
|
||||
"size": 14,
|
||||
"margin": {
|
||||
"right": 6
|
||||
}
|
||||
},
|
||||
"padding": {
|
||||
"left": 8
|
||||
},
|
||||
"corner_radius": 6
|
||||
}
|
||||
},
|
||||
"contact_finder": {
|
||||
|
@ -1249,20 +1249,29 @@
|
||||
"button_width": 8,
|
||||
"icon_width": 8
|
||||
},
|
||||
"row": {
|
||||
"padding": {
|
||||
"left": 8
|
||||
}
|
||||
},
|
||||
"row_height": 28,
|
||||
"header": {
|
||||
"header_row": {
|
||||
"family": "Zed Mono",
|
||||
"color": "#9c9c9c",
|
||||
"size": 14,
|
||||
"margin": {
|
||||
"top": 8
|
||||
},
|
||||
"active": {
|
||||
"family": "Zed Mono",
|
||||
"color": "#f1f1f1",
|
||||
"size": 14,
|
||||
"background": "#1c1c1c"
|
||||
}
|
||||
},
|
||||
"contact_row": {
|
||||
"padding": {
|
||||
"left": 8
|
||||
},
|
||||
"active": {
|
||||
"background": "#1c1c1c"
|
||||
}
|
||||
},
|
||||
"row_height": 28,
|
||||
"tree_branch_color": "#404040",
|
||||
"tree_branch_width": 1,
|
||||
"contact_avatar": {
|
||||
@ -1294,26 +1303,7 @@
|
||||
"button_width": 16,
|
||||
"corner_radius": 8
|
||||
},
|
||||
"project": {
|
||||
"guest_avatar_spacing": 4,
|
||||
"height": 24,
|
||||
"guest_avatar": {
|
||||
"corner_radius": 8,
|
||||
"width": 14
|
||||
},
|
||||
"name": {
|
||||
"family": "Zed Mono",
|
||||
"color": "#474747",
|
||||
"size": 14,
|
||||
"margin": {
|
||||
"right": 6
|
||||
}
|
||||
},
|
||||
"padding": {
|
||||
"left": 8
|
||||
}
|
||||
},
|
||||
"shared_project": {
|
||||
"shared_project_row": {
|
||||
"guest_avatar_spacing": 4,
|
||||
"height": 24,
|
||||
"guest_avatar": {
|
||||
@ -1332,9 +1322,15 @@
|
||||
"left": 8
|
||||
},
|
||||
"background": "#1c1c1c",
|
||||
"corner_radius": 6
|
||||
"corner_radius": 6,
|
||||
"hover": {
|
||||
"background": "#232323"
|
||||
},
|
||||
"active": {
|
||||
"background": "#2b2b2b"
|
||||
}
|
||||
},
|
||||
"hovered_shared_project": {
|
||||
"unshared_project_row": {
|
||||
"guest_avatar_spacing": 4,
|
||||
"height": 24,
|
||||
"guest_avatar": {
|
||||
@ -1352,47 +1348,14 @@
|
||||
"padding": {
|
||||
"left": 8
|
||||
},
|
||||
"background": "#232323",
|
||||
"corner_radius": 6
|
||||
},
|
||||
"unshared_project": {
|
||||
"guest_avatar_spacing": 4,
|
||||
"height": 24,
|
||||
"guest_avatar": {
|
||||
"corner_radius": 8,
|
||||
"width": 14
|
||||
"background": "#1c1c1c",
|
||||
"corner_radius": 6,
|
||||
"hover": {
|
||||
"background": "#232323"
|
||||
},
|
||||
"name": {
|
||||
"family": "Zed Mono",
|
||||
"color": "#474747",
|
||||
"size": 14,
|
||||
"margin": {
|
||||
"right": 6
|
||||
}
|
||||
},
|
||||
"padding": {
|
||||
"left": 8
|
||||
"active": {
|
||||
"background": "#2b2b2b"
|
||||
}
|
||||
},
|
||||
"hovered_unshared_project": {
|
||||
"guest_avatar_spacing": 4,
|
||||
"height": 24,
|
||||
"guest_avatar": {
|
||||
"corner_radius": 8,
|
||||
"width": 14
|
||||
},
|
||||
"name": {
|
||||
"family": "Zed Mono",
|
||||
"color": "#474747",
|
||||
"size": 14,
|
||||
"margin": {
|
||||
"right": 6
|
||||
}
|
||||
},
|
||||
"padding": {
|
||||
"left": 8
|
||||
},
|
||||
"corner_radius": 6
|
||||
}
|
||||
},
|
||||
"contact_finder": {
|
||||
|
@ -1249,20 +1249,29 @@
|
||||
"button_width": 8,
|
||||
"icon_width": 8
|
||||
},
|
||||
"row": {
|
||||
"padding": {
|
||||
"left": 8
|
||||
}
|
||||
},
|
||||
"row_height": 28,
|
||||
"header": {
|
||||
"header_row": {
|
||||
"family": "Zed Mono",
|
||||
"color": "#474747",
|
||||
"size": 14,
|
||||
"margin": {
|
||||
"top": 8
|
||||
},
|
||||
"active": {
|
||||
"family": "Zed Mono",
|
||||
"color": "#2b2b2b",
|
||||
"size": 14,
|
||||
"background": "#d5d5d5"
|
||||
}
|
||||
},
|
||||
"contact_row": {
|
||||
"padding": {
|
||||
"left": 8
|
||||
},
|
||||
"active": {
|
||||
"background": "#d5d5d5"
|
||||
}
|
||||
},
|
||||
"row_height": 28,
|
||||
"tree_branch_color": "#e3e3e3",
|
||||
"tree_branch_width": 1,
|
||||
"contact_avatar": {
|
||||
@ -1294,26 +1303,7 @@
|
||||
"button_width": 16,
|
||||
"corner_radius": 8
|
||||
},
|
||||
"project": {
|
||||
"guest_avatar_spacing": 4,
|
||||
"height": 24,
|
||||
"guest_avatar": {
|
||||
"corner_radius": 8,
|
||||
"width": 14
|
||||
},
|
||||
"name": {
|
||||
"family": "Zed Mono",
|
||||
"color": "#808080",
|
||||
"size": 14,
|
||||
"margin": {
|
||||
"right": 6
|
||||
}
|
||||
},
|
||||
"padding": {
|
||||
"left": 8
|
||||
}
|
||||
},
|
||||
"shared_project": {
|
||||
"shared_project_row": {
|
||||
"guest_avatar_spacing": 4,
|
||||
"height": 24,
|
||||
"guest_avatar": {
|
||||
@ -1332,9 +1322,15 @@
|
||||
"left": 8
|
||||
},
|
||||
"background": "#f8f8f8",
|
||||
"corner_radius": 6
|
||||
"corner_radius": 6,
|
||||
"hover": {
|
||||
"background": "#eaeaea"
|
||||
},
|
||||
"active": {
|
||||
"background": "#e3e3e3"
|
||||
}
|
||||
},
|
||||
"hovered_shared_project": {
|
||||
"unshared_project_row": {
|
||||
"guest_avatar_spacing": 4,
|
||||
"height": 24,
|
||||
"guest_avatar": {
|
||||
@ -1352,47 +1348,14 @@
|
||||
"padding": {
|
||||
"left": 8
|
||||
},
|
||||
"background": "#eaeaea",
|
||||
"corner_radius": 6
|
||||
},
|
||||
"unshared_project": {
|
||||
"guest_avatar_spacing": 4,
|
||||
"height": 24,
|
||||
"guest_avatar": {
|
||||
"corner_radius": 8,
|
||||
"width": 14
|
||||
"background": "#f8f8f8",
|
||||
"corner_radius": 6,
|
||||
"hover": {
|
||||
"background": "#eaeaea"
|
||||
},
|
||||
"name": {
|
||||
"family": "Zed Mono",
|
||||
"color": "#808080",
|
||||
"size": 14,
|
||||
"margin": {
|
||||
"right": 6
|
||||
}
|
||||
},
|
||||
"padding": {
|
||||
"left": 8
|
||||
"active": {
|
||||
"background": "#e3e3e3"
|
||||
}
|
||||
},
|
||||
"hovered_unshared_project": {
|
||||
"guest_avatar_spacing": 4,
|
||||
"height": 24,
|
||||
"guest_avatar": {
|
||||
"corner_radius": 8,
|
||||
"width": 14
|
||||
},
|
||||
"name": {
|
||||
"family": "Zed Mono",
|
||||
"color": "#808080",
|
||||
"size": 14,
|
||||
"margin": {
|
||||
"right": 6
|
||||
}
|
||||
},
|
||||
"padding": {
|
||||
"left": 8
|
||||
},
|
||||
"corner_radius": 6
|
||||
}
|
||||
},
|
||||
"contact_finder": {
|
||||
|
@ -1249,20 +1249,29 @@
|
||||
"button_width": 8,
|
||||
"icon_width": 8
|
||||
},
|
||||
"row": {
|
||||
"padding": {
|
||||
"left": 8
|
||||
}
|
||||
},
|
||||
"row_height": 28,
|
||||
"header": {
|
||||
"header_row": {
|
||||
"family": "Zed Mono",
|
||||
"color": "#93a1a1",
|
||||
"size": 14,
|
||||
"margin": {
|
||||
"top": 8
|
||||
},
|
||||
"active": {
|
||||
"family": "Zed Mono",
|
||||
"color": "#eee8d5",
|
||||
"size": 14,
|
||||
"background": "#586e755c"
|
||||
}
|
||||
},
|
||||
"contact_row": {
|
||||
"padding": {
|
||||
"left": 8
|
||||
},
|
||||
"active": {
|
||||
"background": "#586e755c"
|
||||
}
|
||||
},
|
||||
"row_height": 28,
|
||||
"tree_branch_color": "#657b83",
|
||||
"tree_branch_width": 1,
|
||||
"contact_avatar": {
|
||||
@ -1294,26 +1303,7 @@
|
||||
"button_width": 16,
|
||||
"corner_radius": 8
|
||||
},
|
||||
"project": {
|
||||
"guest_avatar_spacing": 4,
|
||||
"height": 24,
|
||||
"guest_avatar": {
|
||||
"corner_radius": 8,
|
||||
"width": 14
|
||||
},
|
||||
"name": {
|
||||
"family": "Zed Mono",
|
||||
"color": "#839496",
|
||||
"size": 14,
|
||||
"margin": {
|
||||
"right": 6
|
||||
}
|
||||
},
|
||||
"padding": {
|
||||
"left": 8
|
||||
}
|
||||
},
|
||||
"shared_project": {
|
||||
"shared_project_row": {
|
||||
"guest_avatar_spacing": 4,
|
||||
"height": 24,
|
||||
"guest_avatar": {
|
||||
@ -1332,9 +1322,15 @@
|
||||
"left": 8
|
||||
},
|
||||
"background": "#073642",
|
||||
"corner_radius": 6
|
||||
"corner_radius": 6,
|
||||
"hover": {
|
||||
"background": "#586e753d"
|
||||
},
|
||||
"active": {
|
||||
"background": "#586e755c"
|
||||
}
|
||||
},
|
||||
"hovered_shared_project": {
|
||||
"unshared_project_row": {
|
||||
"guest_avatar_spacing": 4,
|
||||
"height": 24,
|
||||
"guest_avatar": {
|
||||
@ -1352,47 +1348,14 @@
|
||||
"padding": {
|
||||
"left": 8
|
||||
},
|
||||
"background": "#586e753d",
|
||||
"corner_radius": 6
|
||||
},
|
||||
"unshared_project": {
|
||||
"guest_avatar_spacing": 4,
|
||||
"height": 24,
|
||||
"guest_avatar": {
|
||||
"corner_radius": 8,
|
||||
"width": 14
|
||||
"background": "#073642",
|
||||
"corner_radius": 6,
|
||||
"hover": {
|
||||
"background": "#586e753d"
|
||||
},
|
||||
"name": {
|
||||
"family": "Zed Mono",
|
||||
"color": "#839496",
|
||||
"size": 14,
|
||||
"margin": {
|
||||
"right": 6
|
||||
}
|
||||
},
|
||||
"padding": {
|
||||
"left": 8
|
||||
"active": {
|
||||
"background": "#586e755c"
|
||||
}
|
||||
},
|
||||
"hovered_unshared_project": {
|
||||
"guest_avatar_spacing": 4,
|
||||
"height": 24,
|
||||
"guest_avatar": {
|
||||
"corner_radius": 8,
|
||||
"width": 14
|
||||
},
|
||||
"name": {
|
||||
"family": "Zed Mono",
|
||||
"color": "#839496",
|
||||
"size": 14,
|
||||
"margin": {
|
||||
"right": 6
|
||||
}
|
||||
},
|
||||
"padding": {
|
||||
"left": 8
|
||||
},
|
||||
"corner_radius": 6
|
||||
}
|
||||
},
|
||||
"contact_finder": {
|
||||
|
@ -1249,20 +1249,29 @@
|
||||
"button_width": 8,
|
||||
"icon_width": 8
|
||||
},
|
||||
"row": {
|
||||
"padding": {
|
||||
"left": 8
|
||||
}
|
||||
},
|
||||
"row_height": 28,
|
||||
"header": {
|
||||
"header_row": {
|
||||
"family": "Zed Mono",
|
||||
"color": "#586e75",
|
||||
"size": 14,
|
||||
"margin": {
|
||||
"top": 8
|
||||
},
|
||||
"active": {
|
||||
"family": "Zed Mono",
|
||||
"color": "#073642",
|
||||
"size": 14,
|
||||
"background": "#93a1a12e"
|
||||
}
|
||||
},
|
||||
"contact_row": {
|
||||
"padding": {
|
||||
"left": 8
|
||||
},
|
||||
"active": {
|
||||
"background": "#93a1a12e"
|
||||
}
|
||||
},
|
||||
"row_height": 28,
|
||||
"tree_branch_color": "#839496",
|
||||
"tree_branch_width": 1,
|
||||
"contact_avatar": {
|
||||
@ -1294,26 +1303,7 @@
|
||||
"button_width": 16,
|
||||
"corner_radius": 8
|
||||
},
|
||||
"project": {
|
||||
"guest_avatar_spacing": 4,
|
||||
"height": 24,
|
||||
"guest_avatar": {
|
||||
"corner_radius": 8,
|
||||
"width": 14
|
||||
},
|
||||
"name": {
|
||||
"family": "Zed Mono",
|
||||
"color": "#657b83",
|
||||
"size": 14,
|
||||
"margin": {
|
||||
"right": 6
|
||||
}
|
||||
},
|
||||
"padding": {
|
||||
"left": 8
|
||||
}
|
||||
},
|
||||
"shared_project": {
|
||||
"shared_project_row": {
|
||||
"guest_avatar_spacing": 4,
|
||||
"height": 24,
|
||||
"guest_avatar": {
|
||||
@ -1332,9 +1322,15 @@
|
||||
"left": 8
|
||||
},
|
||||
"background": "#eee8d5",
|
||||
"corner_radius": 6
|
||||
"corner_radius": 6,
|
||||
"hover": {
|
||||
"background": "#93a1a11f"
|
||||
},
|
||||
"active": {
|
||||
"background": "#93a1a12e"
|
||||
}
|
||||
},
|
||||
"hovered_shared_project": {
|
||||
"unshared_project_row": {
|
||||
"guest_avatar_spacing": 4,
|
||||
"height": 24,
|
||||
"guest_avatar": {
|
||||
@ -1352,47 +1348,14 @@
|
||||
"padding": {
|
||||
"left": 8
|
||||
},
|
||||
"background": "#93a1a11f",
|
||||
"corner_radius": 6
|
||||
},
|
||||
"unshared_project": {
|
||||
"guest_avatar_spacing": 4,
|
||||
"height": 24,
|
||||
"guest_avatar": {
|
||||
"corner_radius": 8,
|
||||
"width": 14
|
||||
"background": "#eee8d5",
|
||||
"corner_radius": 6,
|
||||
"hover": {
|
||||
"background": "#93a1a11f"
|
||||
},
|
||||
"name": {
|
||||
"family": "Zed Mono",
|
||||
"color": "#657b83",
|
||||
"size": 14,
|
||||
"margin": {
|
||||
"right": 6
|
||||
}
|
||||
},
|
||||
"padding": {
|
||||
"left": 8
|
||||
"active": {
|
||||
"background": "#93a1a12e"
|
||||
}
|
||||
},
|
||||
"hovered_unshared_project": {
|
||||
"guest_avatar_spacing": 4,
|
||||
"height": 24,
|
||||
"guest_avatar": {
|
||||
"corner_radius": 8,
|
||||
"width": 14
|
||||
},
|
||||
"name": {
|
||||
"family": "Zed Mono",
|
||||
"color": "#657b83",
|
||||
"size": 14,
|
||||
"margin": {
|
||||
"right": 6
|
||||
}
|
||||
},
|
||||
"padding": {
|
||||
"left": 8
|
||||
},
|
||||
"corner_radius": 6
|
||||
}
|
||||
},
|
||||
"contact_finder": {
|
||||
|
@ -1249,20 +1249,29 @@
|
||||
"button_width": 8,
|
||||
"icon_width": 8
|
||||
},
|
||||
"row": {
|
||||
"padding": {
|
||||
"left": 8
|
||||
}
|
||||
},
|
||||
"row_height": 28,
|
||||
"header": {
|
||||
"header_row": {
|
||||
"family": "Zed Mono",
|
||||
"color": "#979db4",
|
||||
"size": 14,
|
||||
"margin": {
|
||||
"top": 8
|
||||
},
|
||||
"active": {
|
||||
"family": "Zed Mono",
|
||||
"color": "#dfe2f1",
|
||||
"size": 14,
|
||||
"background": "#5e66875c"
|
||||
}
|
||||
},
|
||||
"contact_row": {
|
||||
"padding": {
|
||||
"left": 8
|
||||
},
|
||||
"active": {
|
||||
"background": "#5e66875c"
|
||||
}
|
||||
},
|
||||
"row_height": 28,
|
||||
"tree_branch_color": "#6b7394",
|
||||
"tree_branch_width": 1,
|
||||
"contact_avatar": {
|
||||
@ -1294,26 +1303,7 @@
|
||||
"button_width": 16,
|
||||
"corner_radius": 8
|
||||
},
|
||||
"project": {
|
||||
"guest_avatar_spacing": 4,
|
||||
"height": 24,
|
||||
"guest_avatar": {
|
||||
"corner_radius": 8,
|
||||
"width": 14
|
||||
},
|
||||
"name": {
|
||||
"family": "Zed Mono",
|
||||
"color": "#898ea4",
|
||||
"size": 14,
|
||||
"margin": {
|
||||
"right": 6
|
||||
}
|
||||
},
|
||||
"padding": {
|
||||
"left": 8
|
||||
}
|
||||
},
|
||||
"shared_project": {
|
||||
"shared_project_row": {
|
||||
"guest_avatar_spacing": 4,
|
||||
"height": 24,
|
||||
"guest_avatar": {
|
||||
@ -1332,9 +1322,15 @@
|
||||
"left": 8
|
||||
},
|
||||
"background": "#293256",
|
||||
"corner_radius": 6
|
||||
"corner_radius": 6,
|
||||
"hover": {
|
||||
"background": "#5e66873d"
|
||||
},
|
||||
"active": {
|
||||
"background": "#5e66875c"
|
||||
}
|
||||
},
|
||||
"hovered_shared_project": {
|
||||
"unshared_project_row": {
|
||||
"guest_avatar_spacing": 4,
|
||||
"height": 24,
|
||||
"guest_avatar": {
|
||||
@ -1352,47 +1348,14 @@
|
||||
"padding": {
|
||||
"left": 8
|
||||
},
|
||||
"background": "#5e66873d",
|
||||
"corner_radius": 6
|
||||
},
|
||||
"unshared_project": {
|
||||
"guest_avatar_spacing": 4,
|
||||
"height": 24,
|
||||
"guest_avatar": {
|
||||
"corner_radius": 8,
|
||||
"width": 14
|
||||
"background": "#293256",
|
||||
"corner_radius": 6,
|
||||
"hover": {
|
||||
"background": "#5e66873d"
|
||||
},
|
||||
"name": {
|
||||
"family": "Zed Mono",
|
||||
"color": "#898ea4",
|
||||
"size": 14,
|
||||
"margin": {
|
||||
"right": 6
|
||||
}
|
||||
},
|
||||
"padding": {
|
||||
"left": 8
|
||||
"active": {
|
||||
"background": "#5e66875c"
|
||||
}
|
||||
},
|
||||
"hovered_unshared_project": {
|
||||
"guest_avatar_spacing": 4,
|
||||
"height": 24,
|
||||
"guest_avatar": {
|
||||
"corner_radius": 8,
|
||||
"width": 14
|
||||
},
|
||||
"name": {
|
||||
"family": "Zed Mono",
|
||||
"color": "#898ea4",
|
||||
"size": 14,
|
||||
"margin": {
|
||||
"right": 6
|
||||
}
|
||||
},
|
||||
"padding": {
|
||||
"left": 8
|
||||
},
|
||||
"corner_radius": 6
|
||||
}
|
||||
},
|
||||
"contact_finder": {
|
||||
|
@ -1249,20 +1249,29 @@
|
||||
"button_width": 8,
|
||||
"icon_width": 8
|
||||
},
|
||||
"row": {
|
||||
"padding": {
|
||||
"left": 8
|
||||
}
|
||||
},
|
||||
"row_height": 28,
|
||||
"header": {
|
||||
"header_row": {
|
||||
"family": "Zed Mono",
|
||||
"color": "#5e6687",
|
||||
"size": 14,
|
||||
"margin": {
|
||||
"top": 8
|
||||
},
|
||||
"active": {
|
||||
"family": "Zed Mono",
|
||||
"color": "#293256",
|
||||
"size": 14,
|
||||
"background": "#979db42e"
|
||||
}
|
||||
},
|
||||
"contact_row": {
|
||||
"padding": {
|
||||
"left": 8
|
||||
},
|
||||
"active": {
|
||||
"background": "#979db42e"
|
||||
}
|
||||
},
|
||||
"row_height": 28,
|
||||
"tree_branch_color": "#898ea4",
|
||||
"tree_branch_width": 1,
|
||||
"contact_avatar": {
|
||||
@ -1294,26 +1303,7 @@
|
||||
"button_width": 16,
|
||||
"corner_radius": 8
|
||||
},
|
||||
"project": {
|
||||
"guest_avatar_spacing": 4,
|
||||
"height": 24,
|
||||
"guest_avatar": {
|
||||
"corner_radius": 8,
|
||||
"width": 14
|
||||
},
|
||||
"name": {
|
||||
"family": "Zed Mono",
|
||||
"color": "#6b7394",
|
||||
"size": 14,
|
||||
"margin": {
|
||||
"right": 6
|
||||
}
|
||||
},
|
||||
"padding": {
|
||||
"left": 8
|
||||
}
|
||||
},
|
||||
"shared_project": {
|
||||
"shared_project_row": {
|
||||
"guest_avatar_spacing": 4,
|
||||
"height": 24,
|
||||
"guest_avatar": {
|
||||
@ -1332,9 +1322,15 @@
|
||||
"left": 8
|
||||
},
|
||||
"background": "#dfe2f1",
|
||||
"corner_radius": 6
|
||||
"corner_radius": 6,
|
||||
"hover": {
|
||||
"background": "#979db41f"
|
||||
},
|
||||
"active": {
|
||||
"background": "#979db42e"
|
||||
}
|
||||
},
|
||||
"hovered_shared_project": {
|
||||
"unshared_project_row": {
|
||||
"guest_avatar_spacing": 4,
|
||||
"height": 24,
|
||||
"guest_avatar": {
|
||||
@ -1352,47 +1348,14 @@
|
||||
"padding": {
|
||||
"left": 8
|
||||
},
|
||||
"background": "#979db41f",
|
||||
"corner_radius": 6
|
||||
},
|
||||
"unshared_project": {
|
||||
"guest_avatar_spacing": 4,
|
||||
"height": 24,
|
||||
"guest_avatar": {
|
||||
"corner_radius": 8,
|
||||
"width": 14
|
||||
"background": "#dfe2f1",
|
||||
"corner_radius": 6,
|
||||
"hover": {
|
||||
"background": "#979db41f"
|
||||
},
|
||||
"name": {
|
||||
"family": "Zed Mono",
|
||||
"color": "#6b7394",
|
||||
"size": 14,
|
||||
"margin": {
|
||||
"right": 6
|
||||
}
|
||||
},
|
||||
"padding": {
|
||||
"left": 8
|
||||
"active": {
|
||||
"background": "#979db42e"
|
||||
}
|
||||
},
|
||||
"hovered_unshared_project": {
|
||||
"guest_avatar_spacing": 4,
|
||||
"height": 24,
|
||||
"guest_avatar": {
|
||||
"corner_radius": 8,
|
||||
"width": 14
|
||||
},
|
||||
"name": {
|
||||
"family": "Zed Mono",
|
||||
"color": "#6b7394",
|
||||
"size": 14,
|
||||
"margin": {
|
||||
"right": 6
|
||||
}
|
||||
},
|
||||
"padding": {
|
||||
"left": 8
|
||||
},
|
||||
"corner_radius": 6
|
||||
}
|
||||
},
|
||||
"contact_finder": {
|
||||
|
@ -21,3 +21,8 @@ futures = "0.3"
|
||||
log = "0.4"
|
||||
postage = { version = "0.4.1", features = ["futures-traits"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
|
||||
[dev-dependencies]
|
||||
language = { path = "../language", features = ["test-support"] }
|
||||
project = { path = "../project", features = ["test-support"] }
|
||||
workspace = { path = "../workspace", features = ["test-support"] }
|
||||
|
@ -15,6 +15,7 @@ use serde::Deserialize;
|
||||
use settings::Settings;
|
||||
use std::sync::Arc;
|
||||
use theme::IconButton;
|
||||
use workspace::menu::{SelectNext, SelectPrev};
|
||||
use workspace::{AppState, JoinProject};
|
||||
|
||||
impl_actions!(
|
||||
@ -22,12 +23,13 @@ impl_actions!(
|
||||
[RequestContact, RemoveContact, RespondToContactRequest]
|
||||
);
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
enum ContactEntry {
|
||||
Header(&'static str),
|
||||
IncomingRequest(Arc<User>),
|
||||
OutgoingRequest(Arc<User>),
|
||||
Contact(Arc<Contact>),
|
||||
ContactProject(Arc<Contact>, usize),
|
||||
}
|
||||
|
||||
pub struct ContactsPanel {
|
||||
@ -36,6 +38,7 @@ pub struct ContactsPanel {
|
||||
list_state: ListState,
|
||||
user_store: ModelHandle<UserStore>,
|
||||
filter_editor: ViewHandle<Editor>,
|
||||
selection: Option<usize>,
|
||||
_maintain_contacts: Subscription,
|
||||
}
|
||||
|
||||
@ -57,6 +60,8 @@ pub fn init(cx: &mut MutableAppContext) {
|
||||
cx.add_action(ContactsPanel::remove_contact);
|
||||
cx.add_action(ContactsPanel::respond_to_contact_request);
|
||||
cx.add_action(ContactsPanel::clear_filter);
|
||||
cx.add_action(ContactsPanel::select_next);
|
||||
cx.add_action(ContactsPanel::select_prev);
|
||||
}
|
||||
|
||||
impl ContactsPanel {
|
||||
@ -72,6 +77,7 @@ impl ContactsPanel {
|
||||
|
||||
cx.subscribe(&user_query_editor, |this, _, event, cx| {
|
||||
if let editor::Event::BufferEdited = event {
|
||||
this.selection.take();
|
||||
this.update_entries(cx)
|
||||
}
|
||||
})
|
||||
@ -88,17 +94,20 @@ impl ContactsPanel {
|
||||
let theme = &theme.contacts_panel;
|
||||
let current_user_id =
|
||||
this.user_store.read(cx).current_user().map(|user| user.id);
|
||||
let is_selected = this.selection == Some(ix);
|
||||
|
||||
match &this.entries[ix] {
|
||||
ContactEntry::Header(text) => {
|
||||
Label::new(text.to_string(), theme.header.text.clone())
|
||||
let header_style =
|
||||
theme.header_row.style_for(&Default::default(), is_selected);
|
||||
Label::new(text.to_string(), header_style.text.clone())
|
||||
.contained()
|
||||
.aligned()
|
||||
.left()
|
||||
.constrained()
|
||||
.with_height(theme.row_height)
|
||||
.contained()
|
||||
.with_style(theme.header.container)
|
||||
.with_style(header_style.container)
|
||||
.boxed()
|
||||
}
|
||||
ContactEntry::IncomingRequest(user) => Self::render_contact_request(
|
||||
@ -106,6 +115,7 @@ impl ContactsPanel {
|
||||
this.user_store.clone(),
|
||||
theme,
|
||||
true,
|
||||
is_selected,
|
||||
cx,
|
||||
),
|
||||
ContactEntry::OutgoingRequest(user) => Self::render_contact_request(
|
||||
@ -113,18 +123,36 @@ impl ContactsPanel {
|
||||
this.user_store.clone(),
|
||||
theme,
|
||||
false,
|
||||
is_selected,
|
||||
cx,
|
||||
),
|
||||
ContactEntry::Contact(contact) => Self::render_contact(
|
||||
contact.clone(),
|
||||
current_user_id,
|
||||
app_state.clone(),
|
||||
theme,
|
||||
cx,
|
||||
),
|
||||
ContactEntry::Contact(contact) => {
|
||||
Self::render_contact(contact.clone(), theme, is_selected)
|
||||
}
|
||||
ContactEntry::ContactProject(contact, project_ix) => {
|
||||
let is_last_project_for_contact =
|
||||
this.entries.get(ix + 1).map_or(true, |next| {
|
||||
if let ContactEntry::ContactProject(next_contact, _) = next {
|
||||
next_contact.user.id != contact.user.id
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
Self::render_contact_project(
|
||||
contact.clone(),
|
||||
current_user_id,
|
||||
*project_ix,
|
||||
app_state.clone(),
|
||||
theme,
|
||||
is_last_project_for_contact,
|
||||
is_selected,
|
||||
cx,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
selection: None,
|
||||
entries: Default::default(),
|
||||
match_candidates: Default::default(),
|
||||
filter_editor: user_query_editor,
|
||||
@ -137,175 +165,173 @@ impl ContactsPanel {
|
||||
}
|
||||
|
||||
fn render_contact(
|
||||
contact: Arc<Contact>,
|
||||
theme: &theme::ContactsPanel,
|
||||
is_selected: bool,
|
||||
) -> ElementBox {
|
||||
Flex::row()
|
||||
.with_children(contact.user.avatar.clone().map(|avatar| {
|
||||
Image::new(avatar)
|
||||
.with_style(theme.contact_avatar)
|
||||
.aligned()
|
||||
.left()
|
||||
.boxed()
|
||||
}))
|
||||
.with_child(
|
||||
Label::new(
|
||||
contact.user.github_login.clone(),
|
||||
theme.contact_username.text.clone(),
|
||||
)
|
||||
.contained()
|
||||
.with_style(theme.contact_username.container)
|
||||
.aligned()
|
||||
.left()
|
||||
.boxed(),
|
||||
)
|
||||
.constrained()
|
||||
.with_height(theme.row_height)
|
||||
.contained()
|
||||
.with_style(
|
||||
*theme
|
||||
.contact_row
|
||||
.style_for(&Default::default(), is_selected),
|
||||
)
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn render_contact_project(
|
||||
contact: Arc<Contact>,
|
||||
current_user_id: Option<u64>,
|
||||
project_ix: usize,
|
||||
app_state: Arc<AppState>,
|
||||
theme: &theme::ContactsPanel,
|
||||
is_last_project: bool,
|
||||
is_selected: bool,
|
||||
cx: &mut LayoutContext,
|
||||
) -> ElementBox {
|
||||
let project_count = contact.non_empty_projects().count();
|
||||
let project = &contact.projects[project_ix];
|
||||
let project_id = project.id;
|
||||
|
||||
let font_cache = cx.font_cache();
|
||||
let line_height = theme.unshared_project.name.text.line_height(font_cache);
|
||||
let cap_height = theme.unshared_project.name.text.cap_height(font_cache);
|
||||
let baseline_offset = theme.unshared_project.name.text.baseline_offset(font_cache)
|
||||
+ (theme.unshared_project.height - line_height) / 2.;
|
||||
let tree_branch_width = theme.tree_branch_width;
|
||||
let tree_branch_color = theme.tree_branch_color;
|
||||
let host_avatar_height = theme
|
||||
.contact_avatar
|
||||
.width
|
||||
.or(theme.contact_avatar.height)
|
||||
.unwrap_or(0.);
|
||||
let row = &theme.unshared_project_row.default;
|
||||
let line_height = row.name.text.line_height(font_cache);
|
||||
let cap_height = row.name.text.cap_height(font_cache);
|
||||
let baseline_offset =
|
||||
row.name.text.baseline_offset(font_cache) + (row.height - line_height) / 2.;
|
||||
let tree_branch_width = theme.tree_branch_width;
|
||||
let tree_branch_color = theme.tree_branch_color;
|
||||
|
||||
Flex::column()
|
||||
Flex::row()
|
||||
.with_child(
|
||||
Flex::row()
|
||||
.with_children(contact.user.avatar.clone().map(|avatar| {
|
||||
Image::new(avatar)
|
||||
.with_style(theme.contact_avatar)
|
||||
.aligned()
|
||||
.left()
|
||||
.boxed()
|
||||
}))
|
||||
.with_child(
|
||||
Label::new(
|
||||
contact.user.github_login.clone(),
|
||||
theme.contact_username.text.clone(),
|
||||
)
|
||||
.contained()
|
||||
.with_style(theme.contact_username.container)
|
||||
.aligned()
|
||||
.left()
|
||||
.boxed(),
|
||||
)
|
||||
.constrained()
|
||||
.with_height(theme.row_height)
|
||||
.boxed(),
|
||||
Canvas::new(move |bounds, _, cx| {
|
||||
let start_x = bounds.min_x() + (bounds.width() / 2.) - (tree_branch_width / 2.);
|
||||
let end_x = bounds.max_x();
|
||||
let start_y = bounds.min_y();
|
||||
let end_y = bounds.min_y() + baseline_offset - (cap_height / 2.);
|
||||
|
||||
cx.scene.push_quad(gpui::Quad {
|
||||
bounds: RectF::from_points(
|
||||
vec2f(start_x, start_y),
|
||||
vec2f(
|
||||
start_x + tree_branch_width,
|
||||
if is_last_project {
|
||||
end_y
|
||||
} else {
|
||||
bounds.max_y()
|
||||
},
|
||||
),
|
||||
),
|
||||
background: Some(tree_branch_color),
|
||||
border: gpui::Border::default(),
|
||||
corner_radius: 0.,
|
||||
});
|
||||
cx.scene.push_quad(gpui::Quad {
|
||||
bounds: RectF::from_points(
|
||||
vec2f(start_x, end_y),
|
||||
vec2f(end_x, end_y + tree_branch_width),
|
||||
),
|
||||
background: Some(tree_branch_color),
|
||||
border: gpui::Border::default(),
|
||||
corner_radius: 0.,
|
||||
});
|
||||
})
|
||||
.constrained()
|
||||
.with_width(host_avatar_height)
|
||||
.boxed(),
|
||||
)
|
||||
.with_children(
|
||||
contact
|
||||
.non_empty_projects()
|
||||
.enumerate()
|
||||
.map(|(ix, project)| {
|
||||
let project_id = project.id;
|
||||
.with_child({
|
||||
let is_host = Some(contact.user.id) == current_user_id;
|
||||
let is_guest = !is_host
|
||||
&& project
|
||||
.guests
|
||||
.iter()
|
||||
.any(|guest| Some(guest.id) == current_user_id);
|
||||
let is_shared = project.is_shared;
|
||||
let app_state = app_state.clone();
|
||||
|
||||
MouseEventHandler::new::<JoinProject, _, _>(
|
||||
project_id as usize,
|
||||
cx,
|
||||
|mouse_state, _| {
|
||||
let style = if project.is_shared {
|
||||
&theme.shared_project_row
|
||||
} else {
|
||||
&theme.unshared_project_row
|
||||
}
|
||||
.style_for(mouse_state, is_selected);
|
||||
Flex::row()
|
||||
.with_child(
|
||||
Canvas::new(move |bounds, _, cx| {
|
||||
let start_x = bounds.min_x() + (bounds.width() / 2.)
|
||||
- (tree_branch_width / 2.);
|
||||
let end_x = bounds.max_x();
|
||||
let start_y = bounds.min_y();
|
||||
let end_y =
|
||||
bounds.min_y() + baseline_offset - (cap_height / 2.);
|
||||
|
||||
cx.scene.push_quad(gpui::Quad {
|
||||
bounds: RectF::from_points(
|
||||
vec2f(start_x, start_y),
|
||||
vec2f(
|
||||
start_x + tree_branch_width,
|
||||
if ix + 1 == project_count {
|
||||
end_y
|
||||
} else {
|
||||
bounds.max_y()
|
||||
},
|
||||
),
|
||||
),
|
||||
background: Some(tree_branch_color),
|
||||
border: gpui::Border::default(),
|
||||
corner_radius: 0.,
|
||||
});
|
||||
cx.scene.push_quad(gpui::Quad {
|
||||
bounds: RectF::from_points(
|
||||
vec2f(start_x, end_y),
|
||||
vec2f(end_x, end_y + tree_branch_width),
|
||||
),
|
||||
background: Some(tree_branch_color),
|
||||
border: gpui::Border::default(),
|
||||
corner_radius: 0.,
|
||||
});
|
||||
})
|
||||
.constrained()
|
||||
.with_width(host_avatar_height)
|
||||
Label::new(
|
||||
project.worktree_root_names.join(", "),
|
||||
style.name.text.clone(),
|
||||
)
|
||||
.aligned()
|
||||
.left()
|
||||
.contained()
|
||||
.with_style(style.name.container)
|
||||
.boxed(),
|
||||
)
|
||||
.with_child({
|
||||
let is_host = Some(contact.user.id) == current_user_id;
|
||||
let is_guest = !is_host
|
||||
&& project
|
||||
.guests
|
||||
.iter()
|
||||
.any(|guest| Some(guest.id) == current_user_id);
|
||||
let is_shared = project.is_shared;
|
||||
let app_state = app_state.clone();
|
||||
|
||||
MouseEventHandler::new::<ContactsPanel, _, _>(
|
||||
project_id as usize,
|
||||
cx,
|
||||
|mouse_state, _| {
|
||||
let style = match (project.is_shared, mouse_state.hovered) {
|
||||
(false, false) => &theme.unshared_project,
|
||||
(false, true) => &theme.hovered_unshared_project,
|
||||
(true, false) => &theme.shared_project,
|
||||
(true, true) => &theme.hovered_shared_project,
|
||||
};
|
||||
|
||||
Flex::row()
|
||||
.with_child(
|
||||
Label::new(
|
||||
project.worktree_root_names.join(", "),
|
||||
style.name.text.clone(),
|
||||
)
|
||||
.aligned()
|
||||
.left()
|
||||
.contained()
|
||||
.with_style(style.name.container)
|
||||
.boxed(),
|
||||
)
|
||||
.with_children(project.guests.iter().filter_map(
|
||||
|participant| {
|
||||
participant.avatar.clone().map(|avatar| {
|
||||
Image::new(avatar)
|
||||
.with_style(style.guest_avatar)
|
||||
.aligned()
|
||||
.left()
|
||||
.contained()
|
||||
.with_margin_right(
|
||||
style.guest_avatar_spacing,
|
||||
)
|
||||
.boxed()
|
||||
})
|
||||
},
|
||||
))
|
||||
.contained()
|
||||
.with_style(style.container)
|
||||
.constrained()
|
||||
.with_height(style.height)
|
||||
.boxed()
|
||||
},
|
||||
)
|
||||
.with_cursor_style(if !is_host && is_shared {
|
||||
CursorStyle::PointingHand
|
||||
} else {
|
||||
CursorStyle::Arrow
|
||||
.with_children(project.guests.iter().filter_map(|participant| {
|
||||
participant.avatar.clone().map(|avatar| {
|
||||
Image::new(avatar)
|
||||
.with_style(style.guest_avatar)
|
||||
.aligned()
|
||||
.left()
|
||||
.contained()
|
||||
.with_margin_right(style.guest_avatar_spacing)
|
||||
.boxed()
|
||||
})
|
||||
.on_click(move |_, cx| {
|
||||
if !is_host && !is_guest {
|
||||
cx.dispatch_global_action(JoinProject {
|
||||
project_id,
|
||||
app_state: app_state.clone(),
|
||||
});
|
||||
}
|
||||
})
|
||||
.flex(1., true)
|
||||
.boxed()
|
||||
})
|
||||
}))
|
||||
.contained()
|
||||
.with_style(style.container)
|
||||
.constrained()
|
||||
.with_height(theme.unshared_project.height)
|
||||
.with_height(style.height)
|
||||
.boxed()
|
||||
}),
|
||||
)
|
||||
.contained()
|
||||
.with_style(theme.row.clone())
|
||||
},
|
||||
)
|
||||
.with_cursor_style(if !is_host && is_shared {
|
||||
CursorStyle::PointingHand
|
||||
} else {
|
||||
CursorStyle::Arrow
|
||||
})
|
||||
.on_click(move |_, cx| {
|
||||
if !is_host && !is_guest {
|
||||
cx.dispatch_global_action(JoinProject {
|
||||
project_id,
|
||||
app_state: app_state.clone(),
|
||||
});
|
||||
}
|
||||
})
|
||||
.flex(1., true)
|
||||
.boxed()
|
||||
})
|
||||
.constrained()
|
||||
.with_height(row.height)
|
||||
.boxed()
|
||||
}
|
||||
|
||||
@ -314,6 +340,7 @@ impl ContactsPanel {
|
||||
user_store: ModelHandle<UserStore>,
|
||||
theme: &theme::ContactsPanel,
|
||||
is_incoming: bool,
|
||||
is_selected: bool,
|
||||
cx: &mut LayoutContext,
|
||||
) -> ElementBox {
|
||||
enum Reject {}
|
||||
@ -409,7 +436,11 @@ impl ContactsPanel {
|
||||
row.constrained()
|
||||
.with_height(theme.row_height)
|
||||
.contained()
|
||||
.with_style(theme.row)
|
||||
.with_style(
|
||||
*theme
|
||||
.contact_row
|
||||
.style_for(&Default::default(), is_selected),
|
||||
)
|
||||
.boxed()
|
||||
}
|
||||
|
||||
@ -418,6 +449,7 @@ impl ContactsPanel {
|
||||
let query = self.filter_editor.read(cx).text(cx);
|
||||
let executor = cx.background().clone();
|
||||
|
||||
let prev_selected_entry = self.selection.and_then(|ix| self.entries.get(ix).cloned());
|
||||
self.entries.clear();
|
||||
|
||||
let mut request_entries = Vec::new();
|
||||
@ -443,13 +475,11 @@ impl ContactsPanel {
|
||||
&Default::default(),
|
||||
executor.clone(),
|
||||
));
|
||||
if !matches.is_empty() {
|
||||
request_entries.extend(
|
||||
matches.iter().map(|mat| {
|
||||
ContactEntry::IncomingRequest(incoming[mat.candidate_id].clone())
|
||||
}),
|
||||
);
|
||||
}
|
||||
request_entries.extend(
|
||||
matches
|
||||
.iter()
|
||||
.map(|mat| ContactEntry::IncomingRequest(incoming[mat.candidate_id].clone())),
|
||||
);
|
||||
}
|
||||
|
||||
let outgoing = user_store.outgoing_contact_requests();
|
||||
@ -474,13 +504,11 @@ impl ContactsPanel {
|
||||
&Default::default(),
|
||||
executor.clone(),
|
||||
));
|
||||
if !matches.is_empty() {
|
||||
request_entries.extend(
|
||||
matches.iter().map(|mat| {
|
||||
ContactEntry::OutgoingRequest(outgoing[mat.candidate_id].clone())
|
||||
}),
|
||||
);
|
||||
}
|
||||
request_entries.extend(
|
||||
matches
|
||||
.iter()
|
||||
.map(|mat| ContactEntry::OutgoingRequest(outgoing[mat.candidate_id].clone())),
|
||||
);
|
||||
}
|
||||
|
||||
if !request_entries.is_empty() {
|
||||
@ -515,22 +543,33 @@ impl ContactsPanel {
|
||||
.iter()
|
||||
.partition::<Vec<_>, _>(|mat| contacts[mat.candidate_id].online);
|
||||
|
||||
if !online_contacts.is_empty() {
|
||||
self.entries.push(ContactEntry::Header("Online"));
|
||||
self.entries.extend(
|
||||
online_contacts
|
||||
.into_iter()
|
||||
.map(|mat| ContactEntry::Contact(contacts[mat.candidate_id].clone())),
|
||||
);
|
||||
for (matches, name) in [(online_contacts, "Online"), (offline_contacts, "Offline")] {
|
||||
if !matches.is_empty() {
|
||||
self.entries.push(ContactEntry::Header(name));
|
||||
for mat in matches {
|
||||
let contact = &contacts[mat.candidate_id];
|
||||
self.entries.push(ContactEntry::Contact(contact.clone()));
|
||||
self.entries
|
||||
.extend(contact.projects.iter().enumerate().filter_map(
|
||||
|(ix, project)| {
|
||||
if project.worktree_root_names.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(ContactEntry::ContactProject(contact.clone(), ix))
|
||||
}
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !offline_contacts.is_empty() {
|
||||
self.entries.push(ContactEntry::Header("Offline"));
|
||||
self.entries.extend(
|
||||
offline_contacts
|
||||
.into_iter()
|
||||
.map(|mat| ContactEntry::Contact(contacts[mat.candidate_id].clone())),
|
||||
);
|
||||
if let Some(selection) = &mut self.selection {
|
||||
for (ix, entry) in self.entries.iter().enumerate() {
|
||||
if Some(entry) == prev_selected_entry.as_ref() {
|
||||
*selection = ix;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -566,6 +605,30 @@ impl ContactsPanel {
|
||||
self.filter_editor
|
||||
.update(cx, |editor, cx| editor.set_text("", cx));
|
||||
}
|
||||
|
||||
fn select_next(&mut self, _: &SelectNext, cx: &mut ViewContext<Self>) {
|
||||
if let Some(ix) = self.selection {
|
||||
if self.entries.len() > ix + 1 {
|
||||
self.selection = Some(ix + 1);
|
||||
}
|
||||
} else if !self.entries.is_empty() {
|
||||
self.selection = Some(0);
|
||||
}
|
||||
cx.notify();
|
||||
self.list_state.reset(self.entries.len());
|
||||
}
|
||||
|
||||
fn select_prev(&mut self, _: &SelectPrev, cx: &mut ViewContext<Self>) {
|
||||
if let Some(ix) = self.selection {
|
||||
if ix > 0 {
|
||||
self.selection = Some(ix - 1);
|
||||
} else {
|
||||
self.selection = None;
|
||||
}
|
||||
}
|
||||
cx.notify();
|
||||
self.list_state.reset(self.entries.len());
|
||||
}
|
||||
}
|
||||
|
||||
fn render_icon_button(style: &IconButton, svg_path: &'static str) -> impl Element {
|
||||
@ -633,4 +696,249 @@ impl View for ContactsPanel {
|
||||
.with_style(theme.container)
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
|
||||
cx.focus(&self.filter_editor);
|
||||
}
|
||||
|
||||
fn keymap_context(&self, _: &gpui::AppContext) -> gpui::keymap::Context {
|
||||
let mut cx = Self::default_keymap_context();
|
||||
cx.set.insert("menu".into());
|
||||
cx
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for ContactEntry {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match self {
|
||||
ContactEntry::Header(name_1) => {
|
||||
if let ContactEntry::Header(name_2) = other {
|
||||
return name_1 == name_2;
|
||||
}
|
||||
}
|
||||
ContactEntry::IncomingRequest(user_1) => {
|
||||
if let ContactEntry::IncomingRequest(user_2) = other {
|
||||
return user_1.id == user_2.id;
|
||||
}
|
||||
}
|
||||
ContactEntry::OutgoingRequest(user_1) => {
|
||||
if let ContactEntry::OutgoingRequest(user_2) = other {
|
||||
return user_1.id == user_2.id;
|
||||
}
|
||||
}
|
||||
ContactEntry::Contact(contact_1) => {
|
||||
if let ContactEntry::Contact(contact_2) = other {
|
||||
return contact_1.user.id == contact_2.user.id;
|
||||
}
|
||||
}
|
||||
ContactEntry::ContactProject(contact_1, ix_1) => {
|
||||
if let ContactEntry::ContactProject(contact_2, ix_2) = other {
|
||||
return contact_1.user.id == contact_2.user.id && ix_1 == ix_2;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use client::{proto, test::FakeServer, ChannelList, Client};
|
||||
use gpui::TestAppContext;
|
||||
use language::LanguageRegistry;
|
||||
use theme::ThemeRegistry;
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_contact_panel(cx: &mut TestAppContext) {
|
||||
let (app_state, server) = init(cx).await;
|
||||
let panel = cx.add_view(0, |cx| ContactsPanel::new(app_state.clone(), cx));
|
||||
|
||||
let get_users_request = server.receive::<proto::GetUsers>().await.unwrap();
|
||||
server
|
||||
.respond(
|
||||
get_users_request.receipt(),
|
||||
proto::UsersResponse {
|
||||
users: [
|
||||
"user_zero",
|
||||
"user_one",
|
||||
"user_two",
|
||||
"user_three",
|
||||
"user_four",
|
||||
"user_five",
|
||||
]
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(id, name)| proto::User {
|
||||
id: id as u64,
|
||||
github_login: name.to_string(),
|
||||
..Default::default()
|
||||
})
|
||||
.collect(),
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
server.send(proto::UpdateContacts {
|
||||
incoming_requests: vec![proto::IncomingContactRequest {
|
||||
requester_id: 1,
|
||||
should_notify: false,
|
||||
}],
|
||||
outgoing_requests: vec![2],
|
||||
contacts: vec![
|
||||
proto::Contact {
|
||||
user_id: 3,
|
||||
online: true,
|
||||
projects: vec![proto::ProjectMetadata {
|
||||
id: 101,
|
||||
worktree_root_names: vec!["dir1".to_string()],
|
||||
is_shared: true,
|
||||
guests: vec![2],
|
||||
}],
|
||||
},
|
||||
proto::Contact {
|
||||
user_id: 4,
|
||||
online: true,
|
||||
projects: vec![proto::ProjectMetadata {
|
||||
id: 102,
|
||||
worktree_root_names: vec!["dir2".to_string()],
|
||||
is_shared: true,
|
||||
guests: vec![2],
|
||||
}],
|
||||
},
|
||||
proto::Contact {
|
||||
user_id: 5,
|
||||
online: false,
|
||||
projects: vec![],
|
||||
},
|
||||
],
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
cx.foreground().run_until_parked();
|
||||
assert_eq!(
|
||||
render_to_strings(&panel, cx),
|
||||
&[
|
||||
"+",
|
||||
"v Requests",
|
||||
" incoming user_one <=== selected",
|
||||
" outgoing user_two",
|
||||
"v Online",
|
||||
" user_four",
|
||||
" dir2",
|
||||
" user_three",
|
||||
" dir1",
|
||||
"v Offline",
|
||||
" user_five",
|
||||
]
|
||||
);
|
||||
|
||||
panel.update(cx, |panel, cx| {
|
||||
panel
|
||||
.filter_editor
|
||||
.update(cx, |editor, cx| editor.set_text("f", cx))
|
||||
});
|
||||
cx.foreground().run_until_parked();
|
||||
assert_eq!(
|
||||
render_to_strings(&panel, cx),
|
||||
&[
|
||||
"+",
|
||||
"Online",
|
||||
" user_four <=== selected",
|
||||
" dir2",
|
||||
"Offline",
|
||||
" user_five",
|
||||
]
|
||||
);
|
||||
|
||||
panel.update(cx, |panel, cx| {
|
||||
panel.select_next(&Default::default(), cx);
|
||||
});
|
||||
assert_eq!(
|
||||
render_to_strings(&panel, cx),
|
||||
&[
|
||||
"+",
|
||||
"Online",
|
||||
" user_four",
|
||||
" dir2 <=== selected",
|
||||
"Offline",
|
||||
" user_five",
|
||||
]
|
||||
);
|
||||
|
||||
panel.update(cx, |panel, cx| {
|
||||
panel.select_next(&Default::default(), cx);
|
||||
});
|
||||
assert_eq!(
|
||||
render_to_strings(&panel, cx),
|
||||
&[
|
||||
"+",
|
||||
"Online",
|
||||
" user_four",
|
||||
" dir2",
|
||||
"Offline",
|
||||
" user_five <=== selected",
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
fn render_to_strings(panel: &ViewHandle<ContactsPanel>, cx: &TestAppContext) -> Vec<String> {
|
||||
panel.read_with(cx, |panel, _| {
|
||||
let mut entries = Vec::new();
|
||||
entries.push("+".to_string());
|
||||
entries.extend(panel.entries.iter().map(|entry| match entry {
|
||||
ContactEntry::Header(name) => {
|
||||
format!("{}", name)
|
||||
}
|
||||
ContactEntry::IncomingRequest(user) => {
|
||||
format!(" incoming {}", user.github_login)
|
||||
}
|
||||
ContactEntry::OutgoingRequest(user) => {
|
||||
format!(" outgoing {}", user.github_login)
|
||||
}
|
||||
ContactEntry::Contact(contact) => {
|
||||
format!(" {}", contact.user.github_login)
|
||||
}
|
||||
ContactEntry::ContactProject(contact, project_ix) => {
|
||||
format!(
|
||||
" {}",
|
||||
contact.projects[*project_ix].worktree_root_names.join(", ")
|
||||
)
|
||||
}
|
||||
}));
|
||||
entries
|
||||
})
|
||||
}
|
||||
|
||||
async fn init(cx: &mut TestAppContext) -> (Arc<AppState>, FakeServer) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
let themes = ThemeRegistry::new((), cx.font_cache());
|
||||
let fs = project::FakeFs::new(cx.background().clone());
|
||||
let languages = Arc::new(LanguageRegistry::test());
|
||||
let http_client = client::test::FakeHttpClient::with_404_response();
|
||||
let mut client = Client::new(http_client.clone());
|
||||
let server = FakeServer::for_client(100, &mut client, &cx).await;
|
||||
let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
|
||||
let channel_list =
|
||||
cx.add_model(|cx| ChannelList::new(user_store.clone(), client.clone(), cx));
|
||||
|
||||
let get_channels = server.receive::<proto::GetChannels>().await.unwrap();
|
||||
server
|
||||
.respond(get_channels.receipt(), Default::default())
|
||||
.await;
|
||||
|
||||
(
|
||||
Arc::new(AppState {
|
||||
languages,
|
||||
themes,
|
||||
client,
|
||||
user_store: user_store.clone(),
|
||||
fs,
|
||||
channel_list,
|
||||
build_window_options: || unimplemented!(),
|
||||
build_workspace: |_, _, _| unimplemented!(),
|
||||
}),
|
||||
server,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -235,11 +235,13 @@ pub struct CommandPalette {
|
||||
pub struct ContactsPanel {
|
||||
#[serde(flatten)]
|
||||
pub container: ContainerStyle,
|
||||
pub header: ContainedText,
|
||||
pub user_query_editor: FieldEditor,
|
||||
pub user_query_editor_height: f32,
|
||||
pub add_contact_button: IconButton,
|
||||
pub row: ContainerStyle,
|
||||
pub header_row: Interactive<ContainedText>,
|
||||
pub contact_row: Interactive<ContainerStyle>,
|
||||
pub shared_project_row: Interactive<ProjectRow>,
|
||||
pub unshared_project_row: Interactive<ProjectRow>,
|
||||
pub row_height: f32,
|
||||
pub contact_avatar: ImageStyle,
|
||||
pub contact_username: ContainedText,
|
||||
@ -247,10 +249,6 @@ pub struct ContactsPanel {
|
||||
pub disabled_contact_button: IconButton,
|
||||
pub tree_branch_width: f32,
|
||||
pub tree_branch_color: Color,
|
||||
pub shared_project: ProjectRow,
|
||||
pub hovered_shared_project: ProjectRow,
|
||||
pub unshared_project: ProjectRow,
|
||||
pub hovered_unshared_project: ProjectRow,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default)]
|
||||
|
@ -21,16 +21,6 @@ export default function contactsPanel(theme: Theme) {
|
||||
},
|
||||
};
|
||||
|
||||
const sharedProject = {
|
||||
...project,
|
||||
background: backgroundColor(theme, 300),
|
||||
cornerRadius: 6,
|
||||
name: {
|
||||
...project.name,
|
||||
...text(theme, "mono", "secondary", { size: "sm" }),
|
||||
},
|
||||
};
|
||||
|
||||
const contactButton = {
|
||||
background: backgroundColor(theme, 100),
|
||||
color: iconColor(theme, "primary"),
|
||||
@ -62,14 +52,21 @@ export default function contactsPanel(theme: Theme) {
|
||||
buttonWidth: 8,
|
||||
iconWidth: 8,
|
||||
},
|
||||
row: {
|
||||
padding: { left: 8 },
|
||||
},
|
||||
rowHeight: 28,
|
||||
header: {
|
||||
headerRow: {
|
||||
...text(theme, "mono", "secondary", { size: "sm" }),
|
||||
margin: { top: 8 },
|
||||
active: {
|
||||
...text(theme, "mono", "primary", { size: "sm" }),
|
||||
background: backgroundColor(theme, 100, "active"),
|
||||
}
|
||||
},
|
||||
contactRow: {
|
||||
padding: { left: 8 },
|
||||
active: {
|
||||
background: backgroundColor(theme, 100, "active"),
|
||||
}
|
||||
},
|
||||
rowHeight: 28,
|
||||
treeBranchColor: borderColor(theme, "muted"),
|
||||
treeBranchWidth: 1,
|
||||
contactAvatar: {
|
||||
@ -93,17 +90,35 @@ export default function contactsPanel(theme: Theme) {
|
||||
background: backgroundColor(theme, 100),
|
||||
color: iconColor(theme, "muted"),
|
||||
},
|
||||
project,
|
||||
sharedProject,
|
||||
hoveredSharedProject: {
|
||||
...sharedProject,
|
||||
background: backgroundColor(theme, 300, "hovered"),
|
||||
cornerRadius: 6,
|
||||
},
|
||||
unsharedProject: project,
|
||||
hoveredUnsharedProject: {
|
||||
sharedProjectRow: {
|
||||
...project,
|
||||
background: backgroundColor(theme, 300),
|
||||
cornerRadius: 6,
|
||||
name: {
|
||||
...project.name,
|
||||
...text(theme, "mono", "secondary", { size: "sm" }),
|
||||
},
|
||||
hover: {
|
||||
background: backgroundColor(theme, 300, "hovered"),
|
||||
},
|
||||
active: {
|
||||
background: backgroundColor(theme, 300, "active"),
|
||||
}
|
||||
},
|
||||
unsharedProjectRow: {
|
||||
...project,
|
||||
background: backgroundColor(theme, 300),
|
||||
cornerRadius: 6,
|
||||
name: {
|
||||
...project.name,
|
||||
...text(theme, "mono", "secondary", { size: "sm" }),
|
||||
},
|
||||
hover: {
|
||||
background: backgroundColor(theme, 300, "hovered"),
|
||||
},
|
||||
active: {
|
||||
background: backgroundColor(theme, 300, "active"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user