mirror of https://git.sr.ht/~oppen/ariane
untested tab management
This commit is contained in:
parent
c5fceff48f
commit
0765d85e52
|
@ -7,5 +7,7 @@ sealed class TvaState {
|
|||
data class Requesting(val uri: URI): TvaState()
|
||||
data class NotGeminiRequest(val uri: URI) : TvaState()
|
||||
data class GeminiResponse(val uri: URI, val header: String, val lines: List<String>) : TvaState()
|
||||
data class TabChange(val count: Int) : TvaState()
|
||||
object Blank: TvaState()
|
||||
object GeminiPrintWriterError : TvaState()
|
||||
}
|
|
@ -9,11 +9,15 @@ import java.net.URI
|
|||
|
||||
object NewTabPopup {
|
||||
|
||||
fun show(uri: URI, view: View?, onMenuOption: (menuId: Int) -> Unit){
|
||||
fun show(view: View?, onMenuOption: (menuId: Int) -> Unit){
|
||||
if(view != null) {
|
||||
val popup = PopupMenu(view.context, view)
|
||||
val inflater: MenuInflater = popup.menuInflater
|
||||
inflater.inflate(R.menu.link_menu, popup.menu)
|
||||
popup.setOnMenuItemClickListener { menuItem ->
|
||||
onMenuOption(menuItem.itemId)
|
||||
true
|
||||
}
|
||||
popup.show()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,18 +3,17 @@ package oppen.tva.ui
|
|||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.ViewGroup
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.badge.BadgeDrawable
|
||||
import oppen.alert
|
||||
import oppen.tva.R
|
||||
import oppen.tva.databinding.ActivityTvaBinding
|
||||
import oppen.tva.io.TvaState
|
||||
import oppen.tva.io.history.CacheInterface
|
||||
import oppen.tva.ui.tabs.TabsDialog
|
||||
import oppen.visibleRetainingSpace
|
||||
|
||||
|
||||
|
@ -24,8 +23,13 @@ class TvaActivity : AppCompatActivity() {
|
|||
private lateinit var binding: ActivityTvaBinding
|
||||
private val adapter = GemtextAdapter { uri, longTap, view ->
|
||||
if(longTap){
|
||||
NewTabPopup.show(uri, view){ menuId ->
|
||||
|
||||
NewTabPopup.show(view){ menuId ->
|
||||
when (menuId) {
|
||||
R.id.link_menu_open_in_new_tab -> {
|
||||
model.newTab(uri)
|
||||
}
|
||||
R.id.link_menu_copy -> "Not implemented yet".alert(this)
|
||||
}
|
||||
}
|
||||
}else{
|
||||
model.request(uri)
|
||||
|
@ -48,7 +52,12 @@ class TvaActivity : AppCompatActivity() {
|
|||
is TvaState.Requesting -> loadingView(true)
|
||||
is TvaState.NotGeminiRequest -> externalProtocol(state)
|
||||
is TvaState.GeminiResponse -> renderGemtext(state)
|
||||
TvaState.GeminiPrintWriterError -> TODO()
|
||||
is TvaState.TabChange -> binding.tabCount.text = "${state.count}"
|
||||
is TvaState.Blank -> {
|
||||
binding.addressEdit.setText("")
|
||||
adapter.render(arrayListOf())
|
||||
}
|
||||
TvaState.GeminiPrintWriterError -> "Error with socket writer".alert(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,7 +73,9 @@ class TvaActivity : AppCompatActivity() {
|
|||
|
||||
binding.more.setOnClickListener { "Not implemented yet".alert(this) }
|
||||
binding.home.setOnClickListener { "Not implemented yet".alert(this) }
|
||||
binding.tabs.setOnClickListener { "Not implemented yet".alert(this) }
|
||||
binding.tabs.setOnClickListener {
|
||||
TabsDialog().show(this, model)
|
||||
}
|
||||
}
|
||||
|
||||
private fun externalProtocol(state: TvaState.NotGeminiRequest) {
|
||||
|
|
|
@ -14,26 +14,35 @@ class TvaViewModel: ViewModel() {
|
|||
private var activeTab = 0
|
||||
|
||||
private lateinit var cache: CacheInterface
|
||||
private var tabs = mutableListOf<Tab>()
|
||||
var tabs = mutableListOf<Tab>()
|
||||
|
||||
fun initialise(cache: CacheInterface, onState: (state: TvaState) -> Unit){
|
||||
this.cache = cache
|
||||
this.onState = onState
|
||||
|
||||
|
||||
cache.getTabs { tabs, activeIndex ->
|
||||
this.tabs.addAll(tabs)
|
||||
if(tabs.isEmpty()){
|
||||
this.tabs.add(Tab(0))
|
||||
activeTab = 0
|
||||
request(URI.create("gemini://gemini.circumlunar.space/"))
|
||||
onState(TvaState.TabChange(1))
|
||||
}else{
|
||||
activeTab = activeIndex
|
||||
request(tabs[activeTab].history.last())
|
||||
onState(TvaState.TabChange(tabs.size))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun newTab(uri: URI) {
|
||||
val newTab = Tab(tabs.size)
|
||||
tabs.add(newTab)
|
||||
activeTab = newTab.index
|
||||
onState(TvaState.TabChange(tabs.size))
|
||||
request(uri)
|
||||
}
|
||||
|
||||
fun request(address: String) {
|
||||
request(URI.create(address))
|
||||
}
|
||||
|
@ -56,7 +65,6 @@ class TvaViewModel: ViewModel() {
|
|||
}
|
||||
|
||||
onState(state)
|
||||
|
||||
}
|
||||
|
||||
fun canGoBack(): Boolean {
|
||||
|
@ -82,4 +90,35 @@ class TvaViewModel: ViewModel() {
|
|||
|
||||
|
||||
fun persistTabState() = cache.update(tabs, activeTab)
|
||||
|
||||
fun changeTab(changeIndex: Int) {
|
||||
activeTab = changeIndex
|
||||
request(tabs[activeTab].history.last())
|
||||
}
|
||||
|
||||
fun deleteTab(deleteIndex: Int) {
|
||||
|
||||
if(deleteIndex > activeTab){
|
||||
tabs.removeAt(deleteIndex)
|
||||
onState(TvaState.TabChange(tabs.size))
|
||||
}else if(deleteIndex < activeTab){
|
||||
tabs.removeAt(deleteIndex)
|
||||
activeTab--
|
||||
onState(TvaState.TabChange(tabs.size))
|
||||
}else if(deleteIndex == activeTab){
|
||||
if(tabs.size > 1){
|
||||
tabs.removeAt(deleteIndex)
|
||||
if(activeTab < tabs.size - 1){
|
||||
activeTab--
|
||||
}else{
|
||||
activeTab = tabs.size -1
|
||||
}
|
||||
request(tabs[activeTab].history.last())
|
||||
}else{
|
||||
//Only one tab - we want a fresh state
|
||||
tabs.first().history.clear()
|
||||
onState(TvaState.Blank)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package oppen.tva.ui.tabs
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.android.synthetic.main.grid_cell_tab.view.*
|
||||
import oppen.tva.R
|
||||
import oppen.tva.io.history.Tab
|
||||
|
||||
class TabsAdapter(
|
||||
private val tabs: List<Tab>,
|
||||
val onTabDelete: (index: Int) -> Unit,
|
||||
val onTabChange: (index: Int) -> Unit): RecyclerView.Adapter<TabsAdapter.ViewHolder>() {
|
||||
|
||||
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
return ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.grid_cell_tab, parent, false))
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = tabs.size
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val tab = tabs[position]
|
||||
|
||||
holder.itemView.tab_last_uri.text = "${tab.history.last()}"
|
||||
holder.itemView.remove_tab.setOnClickListener {
|
||||
onTabDelete(holder.adapterPosition)
|
||||
}
|
||||
holder.itemView.tab_cell.setOnClickListener {
|
||||
onTabChange(holder.adapterPosition)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package oppen.tva.ui.tabs
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatDialog
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import kotlinx.android.synthetic.main.dialog_tabs.view.*
|
||||
import oppen.tva.R
|
||||
import oppen.tva.ui.TvaViewModel
|
||||
|
||||
class TabsDialog {
|
||||
|
||||
fun show(context: Context, model: TvaViewModel){
|
||||
val dialog = AppCompatDialog(context, R.style.AppTheme)
|
||||
|
||||
val view = View.inflate(context, R.layout.dialog_tabs, null)
|
||||
dialog.setContentView(view)
|
||||
|
||||
view.close_tab_dialog.setOnClickListener {
|
||||
dialog.dismiss()
|
||||
}
|
||||
|
||||
view.tab_dialog_recycler.layoutManager = GridLayoutManager(context, 2)
|
||||
view.tab_dialog_recycler.adapter = TabsAdapter(model.tabs, { deleteIndex ->
|
||||
model.deleteTab(deleteIndex)
|
||||
dialog.dismiss()
|
||||
}){ changeIndex ->
|
||||
model.changeTab(changeIndex)
|
||||
dialog.dismiss()
|
||||
}
|
||||
|
||||
dialog.show()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M12,2C6.47,2 2,6.47 2,12s4.47,10 10,10 10,-4.47 10,-10S17.53,2 12,2zM17,15.59L15.59,17 12,13.41 8.41,17 7,15.59 10.59,12 7,8.41 8.41,7 12,10.59 15.59,7 17,8.41 13.41,12 17,15.59z"/>
|
||||
</vector>
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
|
||||
</vector>
|
|
@ -0,0 +1,43 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="@dimen/default_margin"
|
||||
android:paddingBottom="@dimen/default_margin">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageButton
|
||||
android:id="@+id/close_tab_dialog"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_margin="@dimen/button_margin"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:src="@drawable/vector_close" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageButton
|
||||
android:id="@+id/tab_dialog_overflow"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:visibility="gone"
|
||||
android:layout_margin="@dimen/button_margin"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:src="@drawable/vector_overflow" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/tab_dialog_recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/header" />
|
||||
|
||||
</RelativeLayout>
|
|
@ -0,0 +1,31 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/tab_cell"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="150dp"
|
||||
android:layout_margin="@dimen/button_margin"
|
||||
android:background="@drawable/drawable_rounded_rect"
|
||||
android:padding="@dimen/default_margin"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:foreground="?android:attr/selectableItemBackgroundBorderless">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageButton
|
||||
android:id="@+id/remove_tab"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:src="@drawable/vector_cancel" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/tab_last_uri"
|
||||
android:layout_centerInParent="true"
|
||||
android:textAlignment="center"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:text="gemini://oppenlab.net" />
|
||||
|
||||
</RelativeLayout>
|
Loading…
Reference in New Issue